Seja Bem-Vindo. Este site tem recursos de leitura de texto, basta marcar o texto e clicar no ícone do alto-falante   Click to listen highlighted text! Seja Bem-Vindo. Este site tem recursos de leitura de texto, basta marcar o texto e clicar no ícone do alto-falante

Aula 04 – Teoria da Computação

Autômatos finitos

No link você pode baixar a apresentação usada em sala de aula. Não estude apenas pela apresentação.

A partir de agora vamos estudar as máquinas que conseguem ler as linguagens que aprendemos anteriormente.

Introdução

Vimos anteriormente que o problema geral da análise sintática é muito difícil. Felizmente, em alguns casos particulares ele se torna mais simples. Nesta seção, vamos estudar um destes casos. Vamos tomar como exemplo a seguinte gramática G:

  • S→0A|1S
  • A→0S|1A|ε 

A notação acima equivale a:

  • S→0A
  • S→1S
  • A→0S
  • A→1A
  • A→ε

Por questão de economia de espaço na página usaremos a primeira notação.

Esta gramática gera as cadeias sobre o alfabeto Σ={0, 1} , que têm quantidade ímpar de caracteres ‘0’. Por exemplo, esta gramática gera a cadeia 01010 da seguinte forma:

S⇒0A⇒01A⇒010S⇒0101S⇒01010A⇒01010

Programar a análise sintática é resolver o problema oposto. Dada a cadeia 01010, podemos determinar se ela está na linguagem gerada pela gramática? Para programar a solução podemos guardar sempre qual é o único não terminal que está no final da cadeia sendo gerada. Se ao final o não terminal for ‘A’, como existe a regra A→ε, então a cadeia estará na linguagem. Esta solução pode ser representada pela ilustração abaixo:

Autômato Finito Determinístico
Autômato Finito Determinístico

Chamamos esta solução de AFD – Autômato Finito Determinístico. A ideia é que cada bola representa um ”estado”. Começamos pelo estado marcado com a seta, no caso, o estado ‘S’. Ao lermos um 0 vamos para o estado ‘A’; ao lermos um ‘1’, permanecemos no estado ‘A’. Depois, ao lermos o segundo ‘0’, voltamos ao estado ‘S’. Ao terminarmos de ler a cadeia de entrada, se estivermos em um estado marcado com círculo duplo, neste caso no estado ‘A’, dizemos que a cadeia foi ‘reconhecida’. Caso contrário, a cadeia não foi reconhecida pelo AFD (GARCIA, 2017).

Em um AFD, dado um estado e um símbolo do alfabeto de entrada, existe uma seta, ou transição para exatamente um único estado. Assim, o AFD está sempre em exatamente um estado, por isso é chamado de determinístico. Observamos que o AFD possui estados. No AFD da figura acima os estados são ‘S’ e ‘A’. O estado ‘S’ é o estado inicial (marcado com uma seta), no qual o AFD inicia sua execução. O estado ‘A’ é um estado final e se o autômato termina em um estado final (marcado com círculo duplo), ele reconheceu com sucesso a cadeia lida. Uma forma alternativa de representar este mesmo AFD é descrito na tabela abaixo:

 

0

1

→S

A

S

*A

S

A

Tabela de uma Autômato Finito Determinístico.

Neste caso, o estado inicial é marcado com a seta. Os estados finais são marcados com o símbolo ‘*’ e, para sabermos para onde vai a seta do estado q com entrada a basta consultar a linha correspondente ao estado q e a coluna correspondente ao símbolo de entrada a . As setas – ou a tabela – representam uma função que, dado um estado e um símbolo da entrada, retorna um único estado.

Atenção

Formalmente um AFD é uma tupla (Q, Σ, δ, q0, F), onde:

  • Q é um conjunto finito e não vazio de estados.
  • Σ é o alfabeto de entrada.
  • δ : Q × Σ → Q é a função de transição de estados.
  • q 0 ∈ Q é o estado inicial
  • F ⊆ Q é o conjunto de estados finais.

No AFD da figura anterior temos que:

  • Q=(S, A);
  • Σ={0, 1};
  • δ:Q×Σ→Q é a função na qual:
    • δ(S, 0)=A, δ(S, 1)=S, δ(A, 0)=S e δ(A, 1)=A
  • S é o estado inicial;
  • {A} é o conjunto de estados finais.

Para entendermos como o AFD funciona é útil estendermos a função δ: Q×Σ→Q para esta extensão, isto é, a função \hat{\delta}, representa a ação do AFD ao ler uma cadeia, ou seja, ao ler mais de um caractere. Sua definição é intuitiva: se ao lermos um 0 temos que δ(S, 0)=A, e o AFD fica no estado A. Ao lermos um segundo 0, temos δ(A, 0)=S e o AFD retorna ao estado S. Isso significa que \hat{\delta}=(S,00)=S. Formalmente podemos definir \hat{\delta} em função de δ da seguinte forma:

  • \boldsymbol{\hat{\delta}}(q , ε)=q, para todo q∈Q.
  • \boldsymbol{\hat{\delta}}(q, aw)=δ(δ (q, a),w), para todo q∈Q, a∈Σ, w∈Σ*.

Aplicando esta definição ao AFD da Figura “Autômato Finito Determinístico”, com entrada 00, temos o seguinte:

  • \boldsymbol{\hat{\delta}}(S, 00)=\boldsymbol{\hat{\delta}}(δ(S ,0), 0)=δ(A, 0)=\boldsymbol{\hat{\delta}}(δ(A ,0),ε)=δ(S, ε)=S

Portanto, a definição funciona como esperávamos.

Finalmente estamos aptos a definir a linguagem gerada por um AFD M. Esta linguagem é formada por todas as cadeias reconhecidas por M, isto é, todas as cadeias que levam M a um estado final.

Dado um AFD M=(Q ,Σ ,δ, q0 ,F) , definimos a linguagem reconhecida por M como:

  • T(M)={w∈Σ*\boldsymbol{\hat{\delta}}(q0, w)∈F}

AFND – Autômato Finito Não Determinístico

No caso do autômato da Figura “Autômato Finito Determinístico” temos que T(M)={0,01,10,011,101,110,000,011,…}, o conjunto de todas as cadeias com número ímpar de caracteres 0. Gostaríamos que o AFD fosse uma boa forma de resolver o problema da análise sintática para todas as linguagens regulares. Entretanto, não é claro como obter um AFD de algumas gramáticas regulares. Por exemplo, a gramática G2 – descrita logo a seguir -, gera as cadeias sobre o alfabeto Σ = {0, 1} que possuem a subcadeia ’00’, isto é, que têm dois caracteres 0 seguidos. G2 possui as regras:

  • S→0S|1S|0A
  • A→0B
  • B→0B|1B|ε

Ao tentarmos transformar esta gramática em um autômato finito obtemos a figura abaixo:

Autômato Finito não Determinístico
Autômato Finito não Determinístico

Podemos dizer que nesta figura a função δ não retorna um estado, mas um conjunto de estados. Por exemplo, δ ( S , 0 ) = { S , A } , enquanto que δ ( A , 1 ) = ∅ , ambos os casos violam a condição de que para cada estado e símbolo de entrada as setas nos levem a exatamente um estado. Neste caso, dizemos que a figura representa um AFND – Autômato Finito Não Determinístico. O AFND da Figura “Autômato Finito não Determinístico” também pode ser representado em outra forma, como apresentado na tabela abaixo:

Representação Tabular do AFND.

Podemos interpretar um AFND como podendo seguir diversos caminhos ao ler uma entrada. Se ao menos 1 dos caminhos levar o AFND a um estado final, dizemos que o AFND reconhece w.

Observe que a função δ neste caso não retorna um estado, mas sim um conjunto de estados, em particular pode ser o conjunto vazio. Assim a assinatura da função deve ser δ: Q×Σ→℘(Q).

O AFND da Figura “Autômato Finito não Determinístico” pode ser transformado em um AFD. Dissemos que um AFND pode seguir diversos caminhos ao ler uma entrada w. Vamos fazer um AFD que guarda os estados de todos esses possíveis caminhos. Como o AFND em questão tem apenas três estados, cada um dos possíveis caminhos seguidos ao lermos uma cadeia w estará em um desses três estados, portanto, os caminhos combinados poderão estar em nenhum estado, ou somente no estado {S}, ou em uma combinação de estados, por exemplo, {S, A, B}. Na pior das hipóteses há 23=8 estados possíveis para esses caminhos combinados. Podemos assim desenhar o AFD da figura abaixo:

AFD obtido a partir de um AFND
AFD obtido a partir de um AFND

Conforme explicado, cada estado do AFD corresponde a todos os possíveis estados que o AFND estaria após ler determinada entrada. Em particular, o estado inicial é {S}, porque o AFND inicia apenas em S. Se δ2 é a função de transição do AFD que desejamos obter, δ2 ({S, A}, 0)=∪q∈{S ,A}δ(q ,0)=δ(S ,0)∪δ(A ,0)={S, A}∪{B}={S ,A ,B}. Os estados finais do AFD correspondem aos conjuntos de estados do AFND que contém ao menos um estado final. No exemplo {S,A,B} e {S,B} são estados finais, porque contém o estado final B.

Dada uma gramática regular sabemos obter um AFND equivalente, a construção anterior nos mostra que dado um AFND podemos obter um AFD equivalente, portanto, para toda gramática regular G sabemos obter um AFD M equivalente, isto é, tal que T(M)=L(G). Isso significa que podemos fazer a análise sintática de forma eficiente para todas as linguagens regulares. Se fizermos um programa que simule um AFD, este consome tempo proporcional ao tamanho da entrada, pois para cada símbolo da cadeia de entrada o AFD precisa fazer um movimento entre dois estados. O consumo de memória, por sua vez, é constante e não depende do tamanho da entrada, basta armazenar a tabela de transição em uma matriz. Entretanto, no AFD obtido na Figura “AFD obtido a partir de um AFND”, é possível diminuir o número de estados sem afetar seu funcionamento. A figura abaixo exibe o mesmo AFD que a figura “AFD obtido a partir de um AFND”. Seus estados foram renomeados para a forma mais usual q0, q1 , … , e desenhamos uma linha tracejada para facilitar o entendimento da melhoria que pode ser feita no AFD.

AFD com Estados Renomeados
AFD com Estados Renomeados

Na figura acim há uma seta que ultrapassa a linha tracejada da esquerda para a direita, e não há seta no sentido oposto. Isto significa que uma vez ultrapassada a seta, não há “volta”, o que corresponde à especificação da linguagem, composta pelas cadeias sobre Σ={0, 1} que possuem a subcadeia ’00’. Uma vez que a subcadeia ’00’ ocorra na cadeia, qualquer continuação da cadeia terá esta característica.

Em um AFD, os estados q e r são equivalentes, se para toda entrada possível o AFD se comporta da mesma forma (reconhece as mesmas entradas) estando em q ou estando em r .

Dado um AFD M=(Q, Σ ,δ ,q0 ,F) , e estados q, r∈Q, definimos que  q e r são equivalentes, ou q ≡ r , quando:

  • ∀w∈Σ*, δ(q ,w)∈F⇔δ(r ,w)∈F

Na figura “AFD com estados renomeados” é apresentado o caso que q 2 ≡ q 3 porque depois da linha tracejada qualquer entrada sempre leva a um estado final. Em geral, é mais fácil identificar estados que não são equivalentes do que estados que são equivalentes. Se fizermos a negação da definição de equivalência temos que:

  • q≢r se, e somente se, ∃w∈Σ *, δ(q, w)∈F⇔δ(r, w)∉F

Esta condição pode ser dividida no caso em que w é a cadeia vazia e o caso onde w não é a cadeia vazia. Se w não é a cadeia vazia e qr, então w=aw2, onde a∈Σ, e δ(q ,a)δ(r ,a), isso significa que a condição de não equivalência pode ser reescrita em dois casos, isto é, qr se um dos seguintes casos é verdadeiro:

  1. q∈F⇔r∉F
  2. ∃a∈Σ, δ(q, a)≢δ(r, a)

A partir disso podemos fazer um algoritmo de minimização que consiste em dois passos:

  1. Marcar como não equivalente todo o par de estados (q, r) no qual q∈F⇔r∉F;
  2. Percorrer todo o par de estados restante, marcando-o como não equivalente quando ∃a∈Σ, δ(q, a)≢δ(r ,a).

Veremos isso através de um exemplo. Considere o AFD da figura abaixo:

AFD a ser minimizado
AFD a ser minimizado

Vamos fazer uma matriz triangular conforme a tabela abaixo, onde cada par de estados é considerado uma única vez. Nesta matriz vamos marcar com um ‘X’ os pares de estados que atendem à primeira condição:q∈F⇔r∉F .

Primeira etapa da minimização do AFD
Primeira etapa da minimização do AFD

Após essa marcação falta analisar dois pares de estados: (q0, q3) e (q1, q2). Começaremos com (q0, q3) . Neste caso, temos que:

  • δ(q0 ,0)=δ(q3 ,0)
  • δ(q0, 1)=q1≢q3=δ(q3 ,1)

Devido ao movimento na segunda linha temos que ∃a∈Σ, δ(q0, a)δ(q3, a) portanto q0q3, podemos marcá-los na tabela “Segunda etapa da minimização do AFD”como não equivalentes:

Segunda etapa da minimização do AFD
Segunda etapa da minimização do AFD

Finalmente, devemos analisar o par (q1, q2). Neste caso, temos que:

  • δ(q1, 0)=δ(q2, 0)
  • δ(q1, 1)=q0≢q3=δ(q2, 1)

Mais uma vez, o movimento da segunda linha faz com que o par não seja equivalente. Portanto, abaixo fica toda marcada com ‘X’, o que significa que não há estados equivalentes, portanto, o AFD não pode ser melhorado.

Terceira etapa da minimização do AFD
Terceira etapa da minimização do AFD

É interessante voltarmos ao final da primeira fase, onde a situação era dada pela tabela “Primeira etapa da minimização do AFD”. Se escolhêssemos analisar primeiro o estado (q1, q2) , devido à condição δ(q1, 1)=q0 e δ(q2, 1)=q3, não saberíamos se (q1, q2) é equivalente ou não porque ainda não sabemos se (q0, q3) o é. Neste caso, devemos colocar um ponteiro de (q0, q3) para (q1, q2) nos alertando que se no futuro formos marcar (q0, q3) como não equivalentes, devemos seguir este ponteiro e marcar também (q1, q2). A situação pode ser desenhada na tabela “Dependência entre estados na minimização do AFD”:

Dependência entre estados na minimização do AFD
Dependência entre estados na minimização do AFD

Depois disso, ao visitarmos (q0, q3) , toda a tabela ficará marcada como não equivalente, conforme obtivemos anteriormente. Note que alguns autores representam a marcação de ponteiros de forma diferente, a escolhida neste texto é aquela que é mais conveniente para a programação. É interessante mostrar mais um exemplo, onde existam estados equivalentes.

Vamos analisar um novo AFD na figura abaixo:

Novo AFD a ser minimizado
Novo AFD a ser minimizado

Ao minimizarmos este AFD, em primeiro lugar devemos marcar como não equivalentes os estados finais com os não finais e vice-versa.

Primeira etapa de minimização do AFD da Figura "Novo AFD a ser minimizado"
Primeira etapa de minimização do AFD da Figura “Novo AFD a ser minimizado”

Falta analisarmos os pares: (q0, q3) e (q1, q2). Começaremos com (q0, q3) . Neste caso, temos que:

  • δ(q0, 0)=q1 e δ(q3, 0)=q2
  • δ(q0, 1)=q2 e δ(q3, 1)=q1

Ambas as linhas dependem do par e devemos indicar a dependência por um ponteiro, obtendo a tabela abaixo:

Segunda etapa de minimização do AFD
Segunda etapa de minimização do AFD

Finalmente analisamos (q1, q2) :

  • δ(q1, 0)=δ(q2, 0)
  • δ(q1, 1)=q0 e δ(q2, 1)=q3

Obtemos uma dependência circular na tabela abaixo:

Terceira etapa de minimização do AFD
Terceira etapa de minimização do AFD

Chegamos ao final do algoritmo e os pares (q0, q3) e (q1, q2) não foram marcados. Podemos considerar todos os pares não marcados como equivalentes. Portanto, podemos juntar (q0, q3) em um único estado, o mesmo se dá para (q1, q2) . Desenhamos o autômato minimizado na figura abaixo:

AFD minimizado
AFD minimizado

Vimos que todo o AFND pode ser transformado em um AFD equivalente e que existe um algoritmo para encontrar o menor AFD possível. Considere o AFD da figura “Autômato Finito Determinístico”. Ele reconhece a linguagem Li0= {w|w tem número ímpar de 0’s}. Como construir um AFD que reconheça o complemento desta linguagem? Basta simplesmente marcar os estados não finais como finais e vice-versa. Assim, as cadeias reconhecidas serão aquelas que não eram reconhecidas anteriormente. O AFD obtido, após renomearmos os estados, é o da figura abaixo.

AFD que reconhece o complemento da linguagem Li0
AFD que reconhece o complemento da linguagem Li0

Uma vez que podemos construir um AFD para toda a linguagem regular e vice-versa, a consequência desta construção é que as linguagens regulares são fechadas sob complemento, isto é, se L é uma linguagem regular, então seu complemento, \overline{L} , também o é. Podemos agora considerar um autômato finito determinístico bem semelhante, que reconhece as cadeias sobre Σ={0, 1} que tem uma quantidade ímpar de 1’s. A figura abaixo exibe este AFD.

AFD que reconhece cadeias com número ímpar de 1 ́s
AFD que reconhece cadeias com número ímpar de 1 ́s

Um problema importante é: dados os AFDs M1 e M2 como fazer um AFD que reconheça a linguagem T(M1)∩T(M2)? Tomando os AFDs M1=(Q1, Σ, δ1, q0, F1) da figura “AFD que reconhece o complemento da linguagem Li0” e M2=(Q2, Σ, δ2, r0, F2) da figura “AFD que reconhece cadeias com número ímpar de 1 ́s”, como fazer um AFD que reconheça as cadeias que tem um número par de 0’s e ímpar de 1’s? Vamos fazer isso construindo o AFD produto, M3=(Q3, Σ, δ3, p0, F3)=M1×M2, no qual o conjunto de estados é o produto cartesiano dos conjuntos de estados dos AFDs originais, isto é, Q3=Q1×Q2 e no qual a função de transição consiste em aplicar as funções de transição em paralelo, isto é, δ3 ((q, r), a)=(δ1(q, a), δ2(r, a)). Escolhemos como estado inicial p0=(q0, r0) e marcamos como estados finais os estados (q, r) onde q∈F1 e r∈F2. O autômato obtido é o da figura abaixo:

AFD produto
AFD produto

Observe que o AFD obtido possui certas simetrias. Sempre que lemos um ‘0’, passamos do lado esquerdo para o lado direito, ou do direito para o esquerdo. Sempre que lemos um ‘1’, passamos da parte de cima para a parte de baixo, ou o contrário. Portanto, a parte da esquerda tem sempre uma quantidade par de 0’s e a parte de baixo tem sempre uma quantidade ímpar de 1’s.

Uma vez que uma linguagem é regular se, e somente se, é reconhecida por um AFD, a construção acima nos assegura que a classe das linguagens regulares é fechada sob a operação de interseção, isto é, dadas as linguagens regulares L1 e L2 , a linguagem L1∩L2 também é uma linguagem regular.

Click to listen highlighted text!