✖

Simulación de un autómata finito no determinista

Si tenemos un autómata no determinista y queremos averiguar si acepta una palabra dada, podemos convertir el autómata en determinista, podemos probar de alguna manera o podemos usar un algoritmo que simule el funcionamiento del AFND.

Principio del algoritmo

Mostraremos el principio con un ejemplo adecuado. Tomemos este autómata:

Se trata de un autómata finito no determinista que acepta cierto lenguaje. Ahora mismo no nos interesa en absoluto cuál. Veremos cómo puede ser el cómputo de un autómata así para la palabra de entrada 11001. El razonamiento será este: procesamos un símbolo de la palabra y miramos adónde podemos ir gracias a las transiciones múltiples para el mismo símbolo. Guardamos todos esos estados y en el paso siguiente supondremos que estamos «en todos esos estados a la vez» y nos interesará adónde podemos llegar desde ellos.

Así, iremos guardando el conjunto de estados en los que podemos encontrarnos y la parte sin leer de la palabra. Al principio tenemos el par ⟨{q0}, 11001⟩. Empezamos en el estado q0 (estado inicial) y en la entrada está la palabra 11001. El autómata queda así; en rojo y en negrita está resaltado el estado en el que estamos:

El primer símbolo es 1; desde el estado q0 hay una sola transición para el símbolo 1, así que llegamos al estado q1. Anotamos el par ⟨{q1}, 1001⟩. El autómata queda así:

Continuamos. La parte sin leer de la palabra es 1001, así que de nuevo buscamos transiciones para 1. Ahora son dos: de vuelta al estado q0 y también al estado q3. Ahora iremos a ambos estados a la vez. Anotamos así el par ⟨{q0, q3}, 001⟩.

¿Qué lógica tiene esto? Sabemos que existe un camino para llegar desde el estado inicial, con la palabra 11, tanto al estado q3 como al estado q0. Todavía no sabemos qué rama nos convendrá, así que sencillamente guardamos ambas. Continuamos. En la entrada tenemos el símbolo 0. Ahora debemos mirar tanto las transiciones desde q0 como las transiciones desde q3. Del estado q0 salen transiciones hacia los estados q1 y q2, y del estado q3 sale una transición de vuelta a q1. Juntamos todos estos estados y obtenemos el par ⟨{q1, q2}, 01⟩:

Pero en este paso todavía no hemos terminado. Vemos que del estado q2 sale una transición ε hacia el estado q4. Sabemos que por una transición ε podemos pasar «en cualquier momento», sin leer otro símbolo. Dicho de otro modo, sin leer otro símbolo de la palabra de entrada, todavía podemos pasar al estado q4. Así añadimos el estado q4 a los estados en los que podemos encontrarnos: ⟨{q1, q2, q4}, 01⟩. Colorearíamos el autómata así:

A continuación, en la entrada hay un 0. Miramos adónde podemos pasar desde esos tres estados en los que estamos. Desde q1 no podemos pasar a ninguna parte, porque falta la transición para 0. El cómputo en esta rama termina. Desde el estado q4 tampoco llegamos a ninguna parte. Desde el estado q2 llegamos de vuelta al estado q2. Pero como desde el estado q2 sale de nuevo una transición ε hacia el estado q4, podemos llegar también al estado q4. Así, desde el conjunto de estados {q1, q2, q4} para el símbolo 0 podemos llegar al conjunto de estados {q2, q4} y obtenemos el par ⟨{q2, q4}, 1⟩:

Por último, en la entrada tenemos el símbolo 1. Desde q2 no llegamos a ninguna parte y el cómputo en esta rama termina. Desde q4, en cambio, llegamos a los estados q3 y q5. Obtenemos el par ⟨{q3, q5}, ε⟩:

El autómata ha leído ya la palabra entera y «se encuentra» en dos estados: q3, q5. Como al menos uno de ellos es un estado final, el autómata acepta la palabra 11001.

Algoritmo

Lo primero que necesitamos es una función que tome como parámetro un conjunto de estados y calcule a qué estados podemos llegar además mediante transiciones ε. La entrada es, por tanto, el autómata ⟨Q, Σ, δ, q0, F⟩ y un conjunto de estados States, y la salida es un superconjunto de esos estados, formado por los estados a los que se puede llegar mediante transiciones ε. A esta operación la llamaremos clausura ε del conjunto States.

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

Usaremos esta función en la función que simulará el funcionamiento del autómata no determinista. La entrada es el autómata, la palabra de entrada w = w1w2… wn y el conjunto de estados en los que se encuentra el autómata en ese momento. La salida es la respuesta SÍ o NO, según el autómata acepte o no la palabra dada.

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

Si queremos comprobar si el autómata acepta la palabra w, llamamos a la función así: ComputeNFA(⟨Q, Σ, δ, q0, F⟩, w, EpsilonClosure(⟨Q, Σ, δ, q0, F⟩, {q0})).

Fuentes

Capítulos: Autómata finito, Autómata completo, Autómata finito no determinista, Simulación de un autómata finito no determinista, Conversión de un AFND en un AFD