Máquina de estados finitos não determinística

Origem: Wikipédia, a enciclopédia livre.
Ir para: navegação, pesquisa

Na teoria da computação, uma máquina de estados finita não-determinística ou um autômato finito não-determinístico (AFND) é uma máquina de estados finita onde para cada par de estado e símbolo de entrada pode haver vários próximos estados possíveis. Isso o distingue do autômato finito determinístico (AFD), onde o próximo estado possível é univocamente determinado. Embora AFD e AFND possuam definições distintas, pode ser mostrado na teoria formal que eles são equivalentes e, deste modo, para qualquer AFND dado, pode-se construir um AFD equivalente e vice-versa: essa é a construção do conjunto das partes. Ambos os tipos de autômatos reconhecem apenas linguagens regulares. Máquinas de estados finitas não-determinísticas são às vezes estudadas com o nome de subshifts de tipo finito.

Máquinas de estados finitas não-determinísticas são generalizadas pelo autômato probabilístico, o qual atribui uma probabilidade para cada transição de estado, e por várias outras maneiras, tais como autômatos com pilha e AFNs com transições ε.

Autômatos finitos não-determinísticos foram introduzidos em 1959 por Michael O. Rabin e Dana Scott,[1] que também mostraram sua equivalência com autômatos finitos determinísticos.

Explicação intuitiva[editar | editar código-fonte]

Um AFND, similarmente a um AFD, lê uma cadeia de símbolos de entrada. Para cada símbolo da entrada há uma transição para um novo estado, até que todos os símbolos de entrada sejam lidos.

Ao contrário de um AFD, há um não-determinismo onde, para cada símbolo de entrada, seu próximo estado pode ser um de dois ou mais estados possíveis. Assim, na definição formal, o próximo estado é um elemento do conjunto das partes dos estados. Este elemento, um conjunto, representa algum subconjunto de todos os possíveis estados que podem ser considerados.

Uma extensão do AFND é o AFND-lambda (também conhecido como AFND-epsilon ou como AFND com transições epsilon), o qual permite uma transição para um novo estado sem ler nenhum símbolo de entrada. Por exemplo, se está no estado 1, com o próximo símbolo de entrada um a, pode-se se mover para o estado 2 sem ler nenhum símbolo de entrada. Assim, tem-se uma ambiguidade: o sistema está no estado 1 ou no estado 2 antes de ler a entrada a? Por causa desta ambiguidade, é mais conveniente falar de um conjunto de possíveis estados que o sistema pode estar. Assim, antes de ler um símbolo de entrada a, o AFND-epsilon pode estar em qualquer um dos estados do conjunto {1,2}. Equivalentemente, pode-se imaginar que o AFND está nos estados 1 e 2 'ao mesmo tempo': isso dá uma ideia informal da construção do conjunto das partes: o AFD equivalente a um AFND é definido como aquele que está no estado q={1,2}. Transições para novo estados sem ler símbolos de entrada são chamadas de transições lambda ou transições epsilon. Elas são comumente rotuladas com as letras gregas λ ou ε.

A noção de aceitação de uma entrada é similar àquela em um AFD. Quando o último símbolo de entrada é lido, diz-se que o AFND aceita se e somente se existe algum conjunto de transições que irá levá-lo a um estado de aceitação. Do mesmo modo, ele rejeita, se, não importando quais transições sejam aplicadas, ele não terminar num estado de aceitação.

Definição formal[editar | editar código-fonte]

Dois tipos similares de AFNDs são comumente definidos: o AFND e o AFND com transições ε. O tipo comum é definido como uma 5-upla, M = (Q, \Sigma, \delta, q_0, F)\,\!, consistindo de

  • um conjunto finito de estados Q;
  • um conjunto finito de símbolos de entrada \Sigma, denominando o alfabeto;
  • uma função de transição \delta:Q \times \Sigma \rightarrow \mathcal{P}(Q);
  • um estado inicial q_0 \in Q;
  • um conjunto distinto de estados F como estados de aceitação (ou estados finais), F \subseteq Q.

Aqui, \mathcal{P}(Q) denota o conjunto das partes de Q

O AFND com transições ε (também conhecido como AFN-epsilon ou AFN-lambda) é definido por uma 5-upla extremamente semelhante, porém, a função de transição é substituída por uma que permite a cadeia vazia ε como uma entrada possível. Desse modo, sua função de transição é dada por:

\delta:Q \times (\Sigma \cup \{ \epsilon \}) \rightarrow \mathcal{P}(Q)

Pode-se mostrar que AFND comum e um AFND com transições ε são equivalentes e, assim, dado qualquer AFND comum, pode-se construir um AFND com transições ε que reconhece a mesma linguagem e vice-versa.

Propriedades dos AFNDs[editar | editar código-fonte]

A máquina começa no estado inicial especificado e lê uma cadeia de símbolos do seu alfabeto. O autômato usa a função de transição de estado \delta para determinar o próximo estado usando o estado atual e o símbolo que acabou de ser lido, o que inclui a cadeia vazia, no caso dos AFNDs com transições ε. Entretanto, "o próximo estado de um AFND não depende somente do evento atual, mas também de um número arbitrário de eventos subsequentes de entradas. Até esses eventos subsequentes ocorrerem, não é possível determinar onde a máquina de estados está".[2]

O conjunto de todas as cadeias que são aceitas por um AFND é a linguagem que o AFND aceita. Esta linguagem deve ser uma linguagem regular.

Para todo AFND pode ser encontrada um AFD que aceite a mesma linguagem. Portanto, é possível converter um AFND existente em um AFD com o propósito de implementar (talvez) uma máquina mais simples. Isto pode ser realizado usando a construção do conjunto das partes, o que pode levar a um aumento exponencial no número de estados necessários. Uma prova formal desta construção do conjunto das partes pode ser encontrada aqui (em inglês).

Muitas vezes, pode ser mais fácil construir um AFND do que um AFD equivalente, tanto pelo entendimento como pelo tamanho da construção. AFNDs são comumente introduzidos no início de cursos de informática teórica para desenvolver a ideia de não-determinismo em modelos computacionais mais poderosos, como as máquina de Turing.

Definição formal de computação dos AFNDs[editar | editar código-fonte]

Seja N = (Q, \Sigma, \delta, q_0, F) um AFND e w \in \Sigma uma cadeia do alfabeto.

Diz-se que N aceita w se podemos escrever w como w=y_1y_2...y_n onde cada y_i \in \Sigma_\epsilon e existe uma sequência de estados r_0r_1..r_n em Q seguindo as três condições a seguir:

  1. r_0 = q_0;
  2. r_{i+1} \in \delta(r_i,y_{i+1}) para  i=  0,1,..., n-1 ;
  3. r_n \in F.

Propriedades do AFND-ε[editar | editar código-fonte]

Para todo p,q\in Q, escreve-se p\stackrel{\epsilon}{\rightarrow}q se e somente se q pode ser alcançado a partir de p através de zero ou mais transições \epsilon. Em outras palavras, p\stackrel{\epsilon}{\rightarrow}q se e somente se existe q_{1}, q_{2},\cdots q_{k}\in Q onde k\geq 0 tal que

q_{1}\in T(p,\epsilon), q_{2}\in T(q_{1},\epsilon),\cdots q_{k}\in T(q_{k-1},\epsilon), q\in T(q_{k},\epsilon).

Para qualquer p\in Q, o conjunto de estados que podem ser alcançados a partir de p é chamado de fecho-epsilon ou fecho-ε de p, e é escrito como

E(\{p\}) = \{ q\in Q : p\stackrel{\epsilon}{\rightarrow}q\}.

Para qualquer subconjunto P\subset Q, define-se o fecho-ε de P como

E(P) = \bigcup\limits_{p\in P}E(\{p\}).

As transições ε são transitivas, de modo que pode ser demonstrado que, para todo q_{0}, q_{1}, q_{2}\in Q e P\subset Q, se q_{1}\in E(\{q_{0}\}) e q_{2}\in E(\{q_{1}\}), então q_{2}\in E(\{q_{0}\}).

Igualmente, se q_{1}\in E(P) e q_{2}\in E(\{q_{1}\}) então q_{2}\in E(P)

Seja x uma cadeia sobre o alfabeto \Sigma \cup \{\epsilon\}. Um AFND-ε M aceita a cadeia x se existe tanto uma representação de x na forma x_1x_2 ... x_n, onde x_i \in \{\Sigma \cup \epsilon \}, como uma sequência de estados p_0,p_1, ..., p_n, onde p_i \in Q, seguindo as seguintes condições:

  1. p_0 \in E(\{q_0\});
  2. p_{i+1} \in E(\delta(p_i,x_{i+1})) para  i=  0,1,..., n-1 ;
  3. p_n \in F.

Implementação[editar | editar código-fonte]

Existem várias maneiras de implementar um AFND:

  • Converter para o AFD equivalente. Em alguns casos isso pode causar um aumento exponencial no tamanho do autômato e, assim, espaço proporcional auxiliar ao número de estados do AFND (como o armazenamento do estado exige no máximo um bit para cada estado no AFND).
  • Manter uma estrutura do conjunto de dados de todos os estados em que a máquina poderia estar. Na leitura do ultimo simbolo de entrada, se um desses estados é um estado final, a máquina aceita a cadeia. No pior dos casos, isso pode exigir espaço auxiliar proporcional ao número de estados do AFND; se a estrutura de conjunto usa um bit por estado do AFND, então esta solução é exatamente equivalente a anterior.
  • Criar múltiplas cópias. Para cada n maneiras de decidir, o AFND cria até n-1 cópias da máquina. Durante sua computação, caso o AFND se depare com uma situação na qual existam múltiplas maneiras de se prosseguir, isto é, a função de transição leve a mais de um estado, pode-se considerar que o AFND divide-se em múltiplas cópias e cada uma dessas cópias fica responsável pela computação de uma das transições possíveis. Quando alguma cópia da máquina chega a uma situação na qual não há transição possível, diz-se que essa cópia do AFND "morre", não mais fazendo parte da computação. Se forma semelhante, caso alguma das cópias do AFND esteja em um estado de aceitação ao final da computação da cadeia de entrada, diz-se que o AFND aceita a dada cadeira. Isso também requer armazenamento linear com respeito ao número de estados do AFND, como também pode haver uma máquina para cada estado do AFND. Dessa forma, pode-se pensar no não-determinismo dos AFNDs como uma computação paralela, na qual o AFND (análogo a um processo) pode se bifurcar (de forma análoga à chamada de sistema Fork).
  • Árvore de possibilidades. Derivada da ideia de ramos de computação, nessa forma, representamos a computação a partir de uma árvore de possibilidades cuja raiz corresponde ao início da computação. A cada símbolo de entrada lido, analisa-se a transição e se criam nós filhos adequadamente, isto é, caso haja 2 estados possíveis em uma dada transição, a árvore ramifica no nível atual e criam-se 2 novos filhos. Analogamente, se houver apenas uma transição possível, a árvore não é ramificada no dado momento e se não houver transição possível, o ramo "morre" e não é mais levado em conta na computação.
  • Explicitamente propague tokens através da estrutura de transição do AFND e combine sempre que um token alcançar um estado final. Isto, às vezes, é útil quando o AFND pode codificar contextos adicionais sobre os eventos que desencadearam da transição. (Para uma implementação que usa esta técnica acompanhe as referências e olhe os Tracematches.[3] )

Exemplo[editar | editar código-fonte]

  • Exemplo 1: O seguinte exemplo explica um AFND M, com um alfabeto binário, que determina se uma entrada contém um número par de 0s ou um número par de 1s (Note que 0 ocorrências é um número para de occorrências). Seja M= (Q, Σ, T, s0, F) onde
  • Σ = {0, 1},
  • Q= {s0, s1, s2, s3, s4},
  • E({s0}) = { s0, s1, s3 }
  • F= {s1, s3}, and
  • A função de transição T pode ser definida por esta tabela de transição de estados:
0
1
ε
S0
{}
{}
{S1, S3}
S1
{S2}
{S1}
{}
S2
{S1}
{S2}
{}
S3
{S3}
{S4}
{}
S4
{S4}
{S3}
{}

O diagrama de estados para M é:

 :NFAexample.svg

M pode ser visto como a união de dois AFDs: um com os estados {S1, S2} e o outro com os estados {S3, S4}.

A linguagem de M pode ser descrita pela linguagem regular dada por esta expressão regular (1^*(01^*01^*)^*) \cup  (0^*(10^*10^*)^*) .

  • Exemplo 2:

Criar o diagrama do AFND da linguagem L = {w | w termina em 00} com 3 estados. Resposta: O estado inicial é de não aceitação e fica em loop recebendo 1's e 0's até que ele "adivinha" de forma não determinística que um 0 recebido é o penúltimo 0 da cadeia, e assim, passa para o segundo estado, que por sua vez, recebe o último zero e passa para o estado final de aceitação, que não tem nenhuma transição partindo dele, e caso recebe um outro símbolo, o ramo da computação morre.

(Exemplo de exercícío não resolvido do Sipser).

  • Exemplo 3:

Criar o diagrama do AFND da linguagem U = { w | w é 0* } com 1 estado. Resposta: O estado inicial é o estado de aceitação e tem apenas 1 transição, que é para ele mesmo, no caso de ler o símbolo 0 da entrada. Caso outro símbolo seja lido, o ramo da computação morre. Neste caso, o não determinismo está associado a não ter uma transição para todos os símbolos do alfabeto. Fazendo um comparativo com o AFD, seria necessário criar um outro estado que é alcançado caso o estado inicial leia 1 da entrada, e que possui um loop para ele mesmo ao receber 0's e 1's.

(Exemplo de exercício não resolvido do Sipser).

  • Exemplo 4:

Criar do diagrama do AFND da linguagem A = { w | w = {0}} com 2 estados. Resposta: O estado inicial é estado de não aceitação e tem uma transição para o estado final de aceitação ao ler o símbolo 0 da entrada. O estado final de aceitação não possui transições para nenhum outro estado. Caso o estado inicial leia o símbolo 1 da entrada ou o estado final ler 0 ou 1, o ramo da computação morre.

(Exemplo de exercício não resolvido do Sipser).

  • Exemplo 5:

Criar o diagrama do AFND da linguagem B = { w | w = vazio } com 1 estado. Resposta: Só há 1 estado que é o estado de aceitação. Não há nenhuma transição. Se o estado receber qualquer símbolo, o ramo da computação morre.

(Exemplo de exercício não resolvido do Sipser).

Aplicação de AFND-ε[editar | editar código-fonte]

AFNDs e AFDs são equivalentes tal que se uma linguagem é reconhecida por um AFND, ela também é reconhecida por um AFD e vice versa. O estabelecimento de tal equivalência é importante e útil. É útil porque construir um AFND que reconhece uma dada linguagem é algumas vezes mais fácil do que construir um AFD para aquela linguagem. Isso é importante porque AFNDs podem ser usadas para reduzir a complexidade do trabalho matemático necessário para estabelecer muitas propriedades importantes na teoria da computação. Por exemplo, é muito mais fácil provar as propriedades que seguem usando AFNDs do que AFDs:

A ideia da prova parte do princípio de que, tendo duas linguagens regulares e os AFNDs que as reconhecem, podemos tomar os dois AFNDs e combiná-los com um terceiro AFND que aceita sua entrada de qualquer um dos dois AFNDs prévios a aceita. A prova formal pode ser encontrada aqui.

Sendo dois AFNDs iniciais A_1 e A_2, a ideia da prova consiste em construir um novo AFND A_{concat} usando a estrutura de A_1 e A_2 na construção. O novo AFND possui como estado inicial, o estado inicial do AFND A_1 e possui toda a estrutura de A_1, com os estados de aceitação de A_1 transformados em estados de não-aceitação possuidores de transições \epsilon para o estado inicial de A_2. O novo AFND, por conseguinte, também possui toda a estrutura de A_2, e seus estados de aceitação são os mesmos de A_2. Dessa forma, a entrada sempre poderá ser quebrada em duas partes não-deterministicamente, e só será aceita se puder ter a primeira parte aceita por A_1 e a segunda por A_2.

Sendo o AFND inicial A_1, é construído um novo AFND A_* que modifica a estrutura de A_1 para fazer com que os estados de aceitação deste possuam, cada um, uma transição \epsilon para o estado inicial de A_1. Adicionalmente, cria-se um novo estado de aceitação que passa a ser o estado inicial de A_*, este possuindo uma transição \epsilon para o estado inicial antigo (estado inicial de A_1).

É importante notar que a construção de A_* a partir da mera adição do estado inicial de A_1 ao conjunto dos estados de aceitação não provaria o que é proposto, pois há casos nos quais certas cadeias indesejadas seriam aceitas. Um exemplo de linguagem cuja computação nessa construção leva a uma falha é a linguagem 1(01)^*.

Notas

  1. Rabin and Scott (1959)
  2. FOLDOC Free Online Dictionary of Computing, Finite State Machine
  3. Allan, C., Avgustinov, P., Christensen, A. S., Hendren, L., Kuzins, S., Lhoták, O., de Moor, O., Sereni, D., Sittampalam, G., and Tibble, J. 2005. Adding trace matching with free variables to AspectJ. In Proceedings of the 20th Annual ACM SIGPLAN Conference on Object Oriented Programming, Systems, Languages, and Applications (San Diego, CA, USA, October 16–20, 2005). OOPSLA '05. ACM, New York, NY, 345-364.

Referências[editar | editar código-fonte]

  • M. O. Rabin and D. Scott, "Finite Automata and their Decision Problems", IBM Journal of Research and Development, 3:2 (1959) pp. 115–125.
  • Michael Sipser, Introduction to the Theory of Computation. PWS, Boston. 1997. ISBN 0-534-94728-X. (see section 1.2: Nondeterminism, pp.47–63.)
  • John E. Hopcroft and Jeffrey D. Ullman, Introduction to Automata Theory, Languages, and Computation, Addison-Wesley Publishing, Reading Massachusetts, 1979. ISBN 0-201-02988-X. (ver capítulo 2)
  • Martin, John C. - Introduction to languages and the theory of computation (2ª Edição)

Softwares[editar | editar código-fonte]

  • Auger - Software brasileiro com interface gráfica para construção e simulação de autômatos finitos e conversão para outros modelos formais.
  • Simulador de Autômatos - Software para criação, teste e conversão de Modelos Formais. Com interface gráfica.
  • SCTMF - Software para Criação e Teste de Modelos Formais.
  • JFlap - Software americano para testes com interface gráfica.

Conceitos relacionados[editar | editar código-fonte]