Search code examples
pythonpython-3.xdfanfa

Non-Deterministic Finite Automata of {ab, abc}* in Python


I have been trying to draw this Non-Deterministic Finite automaton:

NFA with the number of states not exceeding 3 for the language {ab, abc}*, and I reached the solution in the picture below:

NFA diagram

The issue seems to be the code logic since my code always prints "rejected. I will appreciate it if someone can give some hints on how to code this algorithm.

print("Insert below the string: ")
string = str(input())


def state_q0(string):
    if len(string) == 0:
        print("string not accepted")
    else:
        if string[0] == "a":
            state_q1(string[1:])
        elif string[0] == "b":
            print("rejected")


def state_q1(string):
    if len(string) == 0:
        print("string not accepted")
    else:
        if string[1] == "b":
            state_q2(string[2:])
        else:
            print("rejected")


def state_q2(string):
    if len(string) == 0:
        print("string not accepted")
    else:
        if string[2] == "c":
            print("accepted -> q0")
        elif string[2] == "b":
            print("rejected")


state_q0(string)


Solution

  • You should always look at the first character of the string, and call recursive calls using everything except the first character.

    Also it's not noted in your diagram, but I assume that q0 is the accepting state since that corresponds to (ab + abc)*.

    So in your style (which I personally wouldn't use, but ok):

    def q0(s):
        if not s: return "accept"  # Accepting state.
        if s[0] == "a": return q1(s[1:])
        return "reject"
    
    def q1(s):
        if not s: return "reject"
        # NFA, must try both edges.
        if (s[0] == "b" and q0(s[1:]) == "accept" or
            s[0] == "b" and q2(s[1:]) == "accept"):
            return "accept"
        return "reject"
    
    def q2(s):
        if not s: return "reject"
        if s[0] == "c": return q0(s[1:])
        return "reject"
    

    How I would code the NFA however is like such:

    transitions = [
        {"a": {1}},
        {"b": {0, 2}},
        {"c": {0}}
    ]
    starting_state = 0
    accepting_states = {0}
    
    def nfa(w):
        cur_states = {starting_state}
        for c in w:
            if not cur_states: return "reject"
            cur_states = set.union(*
                (transitions[s].get(c, set()) for s in cur_states))
        return "accept" if cur_states & accepting_states else "reject"