✖

Simulating a non-deterministic automaton

Suppose we have a non-deterministic automaton and want to find out whether it accepts a given string. We can convert the automaton to a deterministic one, we can try it somehow or we can use an algorithm that simulates the run of the NFA.

Principle of the algorithm

We will illustrate the principle on a suitable example. Take this automaton:

This is a non-deterministic finite automaton that accepts some language. We do not need to care which one right now. We will show what a computation of such an automaton looks like for the input string 11001. The idea is to process a symbol of the string and look at all the places we can reach thanks to multiple transitions for the same symbol. We remember all these states, and in the next step we assume that we are “in all of these states at once” and we are interested in where we can get to from them.

So we remember the set of states we may be in and the unread part of the string. At the start we have the pair ⟨{q0}, 11001⟩. We start in state q0 (the start state) and the input is the string 11001. The automaton looks like this, with the state we are in highlighted in red and bold:

The first symbol is 1, and from state q0 there is only one transition for the symbol 1, so we get to state q1. We remember the pair ⟨{q1}, 1001⟩. The automaton looks like this:

We continue. The unread part of the string is 1001, so again we look for transitions for 1. There are two this time: back to state q0 and on to state q3. Now we go to both states at once. So we remember the pair ⟨{q0, q3}, 001⟩.

What is the logic of this? We know that there is a path from the start state, using the string 11, to state q3 as well as to state q0. We do not yet know which branch will turn out useful, so we simply remember both. We continue. The next input symbol is 0. Now we have to look at the transitions from q0 as well as those from q3. From state q0 transitions lead to states q1 and q2, and from state q3 a transition leads back to q1. We include all these states, which gives the pair ⟨{q1, q2}, 01⟩:

But we are not finished with this step yet. We can see that an epsilon transition leads from state q2 to state q4. We know that we can take an epsilon transition “at any time” without reading another character. In other words, without reading the next symbol of the input string, we can still move to state q4. So we add state q4 to the states we may be in: ⟨{q1, q2, q4}, 01⟩. We would colour the automaton like this:

Next the input is 0. We look at where we can move from the three states we are in. From q1 we cannot move anywhere, because there is no transition for 0. The computation in this branch ends. From state q4 we cannot get anywhere either. From state q2 we get back to state q2. But because an epsilon transition leads from q2 to state q4 again, we can get to state q4 as well. So for the symbol 0 we can get from the set of states {q1, q2, q4} to the set of states {q2, q4}, which gives the pair ⟨{q2, q4}, 1⟩:

Finally the input symbol is 1. From state q2 we cannot get anywhere, so the computation in this branch ends. But from state q4 we get to states q3 and q5. This gives the pair ⟨{q3, q5}, ε⟩:

The automaton has now read the whole string and is “in” two states: q3, q5. Because at least one of them is an accept state, the automaton accepts the string 11001.

The algorithm

First we need a function that takes a set of states as a parameter and works out which states we can still reach using epsilon transitions. The input is an automaton ⟨Q, Σ, δ, q0, F⟩ and a set of states States, and the output is a superset of these states containing the states reachable using epsilon transitions. We call this operation the epsilon closure of the set States.

FunctionEpsilonClosure(⟨Q,Σ,δ,q0,F⟩,States)NextStates←∅ForEachq∈StatesDoNextStates←NextStates∪{q}∪δ(q,ε)EndForIfNextStates∖States=∅DoReturnStatesElseReturnEpsilonClosure(⟨Q,Σ,δ,q0,F⟩,NextStates)EndIfEndFunction

We use this function inside the function that simulates the run of a non-deterministic automaton. The input is an automaton, an input string w = w1w2… wn and the set of states the automaton is currently in. The output is either the answer Yes or No, depending on whether the automaton accepts the given string.

FunctionComputeNFA(⟨Q,Σ,δ,q0,F⟩,w,States)Ifw=ϵDoIfStates∩F≠∅DoReturnYESElseReturnNOEndIfEndIfNextStates←∅ForEachq∈StatesDoNextStates←NextStates∪δ(q,w1)EndForNextStates←EpsilonClosure(⟨Q,Σ,δ,q0,F⟩,NextStates)ReturnComputeNFA(⟨Q,Σ,δ,q0,F⟩,w2w3…wn,NextStates)EndFunction

If we want to check whether the automaton accepts a string w, we call the function like this: ComputeNFA(⟨Q, Σ, δ, q0, F⟩, w, EpsilonClosure(⟨Q, Σ, δ, q0, F⟩, {q0})).

Sources

Chapters: Finite-state machine, Complete automaton, Non-deterministic finite automaton, Simulating a non-deterministic automaton, Converting an NFA to a DFA