Linguagens e Autómatos
Alfabetos, Palavras e Linguagens
Definimos um alfabeto como um conjunto finito não-vazio (de símbolos). Um alfabeto costuma ser representado pela letra grega .
Exemplo de Alfabeto
Um exemplo de um alfabeto é o conjunto
Definimos uma palavra sobre um alfabeto como uma sequência finita de elementos de . O conjunto de todas as palavras constituídas pelos símbolos do alfabeto é representado por .
Existe uma palavra, a que chamamos palavra vazia e representamos por que satisfaz para qualquer alfabeto .
Exemplo de Palavra
O conjunto de palavras sobre o alfabeto contém, por exemplo, as palavras . Contudo, não contém as palavras , , .
Nota
Para um alfabeto com elementos, o número de palavras de tamanho sobre esse alfabeto é .
Nomeadamente, a única () palavra de tamanho é a palavra vazia .
Operações sobre palavras
Para uma palavra definimos a operação como a operação de reversão da palavra.
Por exemplo, para , temos que .
Definimos ainda a operação binária de concatenação .
Por exemplo, para e temos que .
Temos nomeadamente que, .
Note-se que esta operação não é comutativa, mas é associativa.
Para , denotamos por à palavra que resulta da concatenação de consigo própria vezes. Mais precisamente,
Usamos ainda a notação para representar o comprimento da palavra .
Por exemplo, e .
Para qualquer , usamos para nos referir ao -ésimo símbolo da palavra .
Por exemplo, para tem-se que , e .
Uma linguagem sobre o alfabeto é qualquer conjunto .
Operações sobre Linguagens
Damos o nome de linguagem complementar de à linguagem .
Denotamos por o conjunto de todas as linguagens sobre .
Dadas duas linguagens , definimos a concatenação das linguagens como sendo a linguagem .
Definimos ainda o fecho de Kleene de uma linguagem à linguagem
Exemplo de Linguagem
Um exemplo de uma linguagem sobre o alfabeto é as palavras que acabam com exatamente três 's.
As linguagens no sentido mais corrente da palavra (Português, Inglês, Mandarim) ou mesmo as linguagens de programação são linguagens de acordo com esta definição. Têm um alfabeto (no caso do português, corresponde às letras - minúsculas, maiúsculas, acentuadas e não acentuadas -, bem como outros símbolos - !, ?, ., por exemplo) que, quando de acordo com uma regra (muito complexa, claro) formam palavras "aceites", isto é, palavras que estão de acordo com as regras da linguagem.
Autómatos
Autómatos Finitos Determinísticos
Um autómato finito determinístico (AFD) é definido como um quíntuplo tal que
- é um alfabeto;
- é um conjunto finito não vazio de estados;
- é o estado inicial;
- é um conjunto de estados finais;
- é uma função de transição.
Cada AFD define uma linguagem sobre o seu alfabeto .
Dizemos que um autómato é total se a função de transição estiver definida para todo o elemento em , isto é, se a função de transição em cada estado estiver definida para todas as letras.
Um autómato não total pode ser convertido num autómato total da seguinte forma:
- adiciona-se um estado não final ;
- estende-se a função de transição tal que para todo o par tal que a função de transição não fosse definida;
- define-se , para todo o . É conveniente pensar neste estado como um "estado lixo".
Para um AFD definimos a função de transição estendida deste autómato como a função tal que
para cada , e .
Entenda-se esta função como a função que, dado um estado inicial e uma palavra, devolve o estado que resulta da aplicação da função de transição do autómato às sucessivas letras da palavra.
Dizemos que uma palavra é aceite por um AFD se .
Ao conjunto das palavras aceites por um AFD , damos o nome de linguagem reconhecida por esse AFD.
Grafos de AFD's
Estas entidades são normalmente representadas e entendidas de acordo com grafos como o que mostramos abaixo. Para caracterizar rigorosamente esta linguagem é útil considerar a extensão a da função de transição do AFD.
Este gráfico representa o autómato cujo:
- alfabeto é
- conjunto de estados é ;
- estado inicial é ;
- conjunto de estados finais é ;
- função de transição é tal que
Mais genericamente, a representação gráfica de um autómato é tal que:
- os estados correspondem aos vértices do grafo;
- o estado inicial é aquele em que entra a seta sem origem ;
- os estados finais são os rodeados;
- as arestas (dirigidas) indicam a definição da função .
Uma linguagem diz-se regular se existe um AFD com alfabeto tal que . Ou seja, uma linguagem é regular se for reconhecida por um AFD. Denota-se por o conjunto de todas as linguagens regulares com alfabeto .
Usa-se apenas em vez de sempre que o alfabeto esteja subentendido ou não seja importante o contexto.
Equivalência e Minimização de AFD's
Dizemos que dois AFD's são equivalentes se reconhecerem a mesma linguagem.
Para estudar a equivalência de AFD's, introduzimos as seguintes definições:
Para um AFD dizemos que um estado é:
- acessível se existe tal que . Por outras palavras, um estado é acessível se for alcançável a partir da origem;
- produtivo se existe tal que . Por outras palavras, um estado é produtivo se for possível chegar a um estado final a partir dele;
- útil se for acessível e produtivo, inútil caso contrário.
Introduzimos abaixo o algoritmo de procura de estados notáveis (APEN):
APEN
Apresentamos agora um algoritmo para a procura de estados notáveis.
O algoritmo recebe como input um AFD e dá como output um tuplo com, respetivamente, os estados acessíveis, produtivos, úteis e inúteis de .
- ;
- ;
- enquanto
- ;
- ;
Estados acessíveis determinados
- ;
- ;
- enquanto
- ;
- ;
Estados produtivos determinados
- ;
- .
Estados úteis e inúteis determinados
Temos que a execução deste algoritmo termina sempre e identifica corretamente os estados acessíveis (), produtivos (), úteis () e inúteis ().
Vamos então tentar compreender o que o algoritmo acima está a fazer:
- Numa primeira fase (passos 1 a 3), vamos descobrir quais são os estados acessíveis. Intuitivamente, podemos fazer isto começando em e fazendo uma procura (BFS) no grafo do AFD. À medida que o fazemos, colocamos esses estados no conjunto de estados acessíveis;
- Numa segunda fase (passos 4 a 6), vamos determinar os estados produtivos. Estes são aqueles que vão levar a estados finais. Então, começamos exatamente nos estados finais e fazemos também uma procura (BFS), mas desta vez no sentido contrário das setas do grafo do AFD. À medida que descobrimos os estados produtivos, colocamo-los no conjunto apropriado.
- Finalmente, determinamos os estados úteis e inúteis de acordo com a sua definição.
Para facilitar a compreensão do algoritmo, pode ser útil vê-lo em prática no exemplo seguinte.
Exemplo de aplicação do APEN
Tenhamos um AFD tal que:
Ora, procurando seguir os passos descritos na descrição acima:
-
Descobrir os estados acessíveis passa por realizar uma BFS a partir do estado inicial, - todos os estados encontrados dizem-se acessíveis:
Começamos com o conjunto de estados acessíveis a conter apenas :
Logo de seguida, começamos a BFS partindo desse mesmo estado:
Encontrámos, a distância do estado inicial, os estados . A BFS continua então, partindo desses mesmos estados, tal que:
Podemos observar que a procura encontrou aqui . Mais ainda, temos que não há mais caminhos por onde prosseguir. A procura termina, portanto, e o conjunto de estados acessíveis foi obtido.
-
De seguida, determinar os estados produtivos: fazer BFS's, partindo de cada estado final, pelo "autómato transposto":
Inicialmente, o grafo transposto encontra-se assim (os estados finais estão, claro, no conjunto dos estados produtivos):
Realizamos aqui o primeiro passo da BFS - partindo dos estados finais, e , realizamos uma procura pelos estados a que podemos chegar a partir deles:
Repetimos o passo anterior, desta vez partindo dos estados que obtivemos acima: e :
A partir dos estados acima obtidos, não podemos atingir qualquer outro estado, pelo que o algoritmo pára e temos determinado o conjunto de estados produtivos do autómato.
Ora, temos então dois conjuntos em mãos:
Pela definição da utilidade de um estado (um estado diz-se útil caso seja acessível e produtivo), podemos dizer que a interseção dos conjuntos acima corresponde ao conjunto dos estados úteis do autómato, e que portanto:
Prova da correção do algoritmo APEN
Resume-se essencialmente a: BFS funciona.
Definimos agora dois estados de um AFD como
- equivalentes se, para cada , temos que ;
- distinguíveis se não forem equivalentes. Neste caso existe pelo menos uma palavra tal que (ou vice-versa). Dizemos que distingue e ;
Vamos agora identificar critérios para distinguir estados. A partir disto, poderemos descrever um algoritmo para encontrar estados equivalentes e, consequentemente, determinar um algoritmo que minimize um AFD.
Seja um AFD e sejam e . Temos que:
- Se , e então e são distinguíveis;
- Se é produtivo e não, então e são distinguíveis;
- Se é produtivo e não está definido, então e são distinguíveis;
- Se e são estados distinguíveis, e , então e são distinguíveis. Equivalentemente, se e são equivalentes, então e também o são para qualquer ;
Ver a prova destas propriedades pode ajudar a compreendê-las.
Prova
- A palavra distingue os estados;
- Se é produtivo, existe tal que . Contudo, como não é produtivo, , pelo que a palavra distingue os estados;
- Sabemos que podemos adicionar um estado lixo ao AFD de forma que ficamos com um AFD equivalente. Se não está definido, no autómato com estado lixo vamos ver que , que é um estado não produtivo. Podemos então aplicar a condição dois a e ;
- Seja uma palavra que distingue e . Então, a palavra distingue e .
Definimos ainda como sendo o conjunto de todos os pares .
Tendo em conta estes critérios e este conjunto, introduzimos agora um algoritmo de procura de estados distinguíveis (APED).
APED
O algoritmo recebe como input o AFD e o seu output é um conjunto de pares de estados distinguíveis.
-
- ;
- enquanto e
- ;
- ;
- .
O que o algoritmo acima faz é o seguinte:
Para começar, determinar os estados distinguíveis de acordo com os 3 primeiros critérios.
Então, enquanto os houver, determinar mais estados distinguíveis de acordo com o 4º critério.
À medida que estes estados vão sendo encontrados, vão sendo agrupados no conjunto .
A execução deste algoritmo termina sempre retornando exatamente o conjunto de pares de estados distinguíveis do AFD.
Para ajudar a compreender este algoritmo pode ser útil vê-lo em prática abaixo.
Exemplo de aplicação do APED
Consideremos um AFD tal que:
A nossa primeira tarefa será preencher segundo os três critérios iniciais:
- Num primeiro momento, organizar pares onde um elemento é um estado final e o outro é um estado não final (a ordem é irrelevante) - temos, neste momento:
-
De seguida, criar pares onde um elemento é um estado produtivo e o outro não - fazendo a BFS mencionada acima, verificamos que todos os estados são produtivos, pelo que permanece igual.
-
Por fim, encontrar todos os pares tais que um dos vértices transita, segundo um dado símbolo, para um estado produtivo, e o outro não tem qualquer transição associada a esse símbolo. Verificar esta condição pode ser mais fácil seguindo algumas heurísticas:
-
Num primeiro momento, verificar todos os estados para os quais nem todos os símbolos têm uma transição definida - aqui, não tem transição definida para e , e é o único nessa situação. Podemos a partir daqui depreender que qualquer par obtido através desta "procura" terá de envolver .
-
Obtidos símbolos sem transição definida para , aqui e , procuramos os estados que têm transição definida para os mesmos e onde essa transição leve a um estado produtivo. Neste caso, tem transições segundo e para estados produtivos, tais como e , pelo que podemos admitir que os pares criados por esta procura são .
No final destes três passos, ficamos com:
-
O resto do algoritmo normalmente faz-se recorrendo a uma tabela, onde cada linha e coluna correspondem a um dos estados do autómato (só se utiliza metade da tabela, já que é simétrica). A tabela corresponde ao autómato em questão seria:
Cada entrada na tabela corresponde a um dos pares de estados possíveis. Começamos por preencher a tabela com uma cruz em cada entrada que corresponde a um par em . Seria, portanto:
Entramos aqui na secção porventura mais desagradável: percorrer todos os pares de (que tenham uma cruz na tabela, portanto), e para cada um deles, verificar se existe um par que não esteja em tal que, segundo transições por um mesmo símbolo, chegam ao par de estados original (e adicionar qualquer estado encontrado à tabela). Assim que um par é (ou não) encontrado, a cruz na tabela é rodeada por um círculo (para anotar que já foi explorado).
Ora, procuremos então percorrer:
- todos os estados que incluem não adicionam pares a , já que não há qualquer estado a transitar para sequer. A tabela fica, então:
- olhando para o estado , podemos notar que não há qualquer par de estados que não esteja em e em que, segundo o mesmo símbolo, leve ao estado . Não existe qualquer estado segundo a transitar para , nem nenhum estado que segundo ou transite para , pelo que o estado dá-se por explorado sem adicionar nada de novo à tabela (sem ser circular a cruz respetiva a ).
- por fim, resta explorar . Não existe qualquer estado segundo a transitar para , nem nenhum estado que segundo ou transite para , pelo que o estado dá-se por explorado sem adicionar nada de novo à tabela (sem ser circular a cruz respetiva a ).
Todas as entradas com cruzes na tabela foram oficialmente exploradas. Os estados distinguíveis correspondem, então, às entradas com cruzes da tabela: podemos afirmar que todos os pares de estados do autómato, exceto , são distinguíveis entre si.
Além de permitir calcular o conjunto dos pares de estados distinguíveis de um AFD e, consequentemente, o conjunto dos seus pares de estados equivalentes, o APED pode também ser usado para determinar se dois AFD's e são ou não equivalentes.
Isto é feito através do algoritmo de teste à equivalência de AFD's (ATEQ):
ATEQ
O algoritmo recebe como input dois AFD's e tais que , e o seu output é um booleano que indica se os AFD's são ou não equivalentes.
- construir o AFD em que
- ;
- se
return false
caso contrárioreturn true
.
Este algoritmo termina sempre, identificando como equivalentes dois AFD's se e só se eles são equivalentes.
Prova da correção do ATEQ
Verifica-se que, se , então , para qualquer .
Indutivamente, temos então que para qualquer , se , então .
Consequentemente, se para , então .
Estas propriedades verificam-se analogamente para .
Podemos inferir então que equivale a dizer que existe uma palavra tal que apenas um dos dois se verifica:
- ;
- .
consequentemente, já que e , apenas um dos dois se verifica:
- ;
- .
ou seja, a palavra é reconhecida exatamente num dos autómatos e .
Estamos agora prontos para resolver problemas de minimização de AFD's.
Um AFD diz-se mínimo se não existir nenhum outro AFD que lhe seja equivalente e tenha menos estados.
Para determinar o AFD mínimo a um AFD definimos a partição induzida pelos estados equivalentes de como o conjunto em que , para cada .
Note-se como para e distinguíveis e
, pelo que o conjunto definido é de facto uma partição.
Estamos agora em posição de introduzir o algoritmo de minimização de um AFD:
Minimização de AFD
Dado um AFD a minimização de é o AFD construído da seguinte forma:
- se , então tem apenas um estado inicial não final, e a sua função de transição não está definida para qualquer letra;
- caso contrário, em que:
- é a partição do conjunto dos estados úteis de induzida pelos estados equivalentes, com ;
- ;
- ;
- é tal que:
Observações
É relevante para o algoritmo acima notar que, para um conjunto de estados equivalentes:
-
Se existe um estado produtivo em , então só tem estados produtivos. Assim sendo, ao ignorarmos estados inúteis não podemos ignorar estados inúteis por equivalência;
-
Se , então . Isto é, se há um estado final em , todos os estados de são finais. Isto é uma consequência direta do primeiro critério de distinção de estados e faz com que a definição de estados finais de seja não ambígua;
-
Se para e , então, para cada temos que um dos dois se verifica:
- não está definido e não é produtivo;
- .
Isto faz com que a função esteja bem definida. Note-se que desta propriedade resulta que não pode oferecer dois valores diferentes, nem pode ser indefinida para alguns valores e definida para outros (uma vez que em consideramos apenas estados úteis).
Prova de equivalência de a
Seja tal que .
Se , então .
Caso contrário, para e .
Segundo o quarto critério de distinção de estados, temos que se então .
Assim sendo, por indução, temos que se , então .
Nomeadamente, uma palavra que seja aceite em , será também aceite em .
Explicado de uma forma menos rigorosa, o que estamos a constatar é que à medida que vai "lendo símbolos do alfabeto", o AFD está sempre num estado que corresponde à "classe de equivalência" do estado em que está depois de ler esses mesmos símbolos.
Ora, se ao fim dessa leitura, está num estado final, e as classes de equivalência de estados finais em são estados finais em , temos que ao fim dessa mesma leitura, também está num estado final.
Quer isto dizer que qualquer palavra aceite por é também aceite por .
Exemplo da Minimização de um AFD
Consideremos o seguinte autómato:
Apesar do decorrer do algoritmo de procura de estados distinguíveis não constar deste exemplo, consideremos que, aquando do concluir do mesmo, a tabela é tal que:
A tabela final tem, portanto, dois pares de estados equivalentes: e . Ao desenhar o autómato minimizado, os estados presentes em cada par terão de estar juntos.
Pode agora ser mais claro o porquê de considerarmos dois estados equivalentes/distinguíveis: os estados equivalentes têm, no autómato original, transições equivalentes (segundo o mesmo símbolo vão sempre para um estado num "grupo de estados equivalentes"). No caso de , por exemplo, temos que:
- através de transitam para o próprio estado em ambas as situações;
- através de transitam, em ambos os casos, para ;
- através de transitam, em ambos os casos, para um estado dentro do "par equivalente" - para e para .
Autómatos Finitos Não Determinísticos
Introduzimos a notação como o conjunto dos subconjuntos do conjunto . Também se diz que este é o conjunto das partes de .
Exemplo de um Conjunto de Partes
Temos, por exemplo, que .
Tamanho do conjunto das partes
Se um conjunto tem elementos, o conjunto tem elementos.
Prova
Isto é uma consequência de cada conjunto estar unicamente determinado pelos elementos que têm. Ora, para cada elemento, ele ou está, ou não está num dado conjunto - isto é, cada elemento tem 2 opções em relação a um dado conjunto. Sendo assim, deve haver subconjuntos de um conjunto de elementos.
Esta prova pode ser formalizada por indução.
Um autómato finito não determinístico (AFND) é definido como um quíntuplo tal que:
- é um alfabeto;
- é um conjunto finito de estados;
- é o estado inicial;
- é um conjunto de estados finais;
- é uma função de transição.
Note-se que a diferença entre um AFND e um AFD é que a função de transição num AFD não é determinística, na medida em que não define um e um só estado. Para cada par temos que define o subconjunto de dos estados que podem resultar da transição por a partir de .
Podemos ainda assinalar o autómato finito não determinístico como AFND se a função de transição tiver como domínio . Ou seja, um AFND é tal que pode haver transições que não são "causadas" por letra nenhuma. Nesta situação diz-se que o AFND tem movimentos-.
A distinção entre AFND e AFND é negligenciada em contextos que não seja relevante. Para simplificar, pode-se assumir que um AFND está sempre dotado de movimentos-.
Grafo de um AFND
Este gráfico representa o autómato cujo:
- alfabeto é
- conjunto de estados é ;
- estado inicial é ;
- conjunto de estados finais é ;
- função de transição é tal que
Para isto, começamos por definir o fecho- de um estado.
Seja um AFND. O fecho- de um estado é o conjunto tal que:
- ;
- .
Dado um subconjunto de , o seu fecho- é o conjunto .
Dito de forma corrente, o fecho- de um estado é o conjunto de estados que se conseguem obter a partir de apenas com movimentos-.
Podemos então definir a função de transição estendida de um AFND como a função tal que, para cada , e :
Através desta função, podemos definir a palavra aceite por um AFND como qualquer palavra tal que .
Dito de forma corrente, uma palavra é aceite por um AFND se houver uma sequência de estados em tal que:
- a concatenação dos símbolos das transições entre esses estados resulte na palavra em questão;
- a sequência acabe num estado final.
Exemplo de Palavra Aceite num AFND
Consideremos o AFND que aceita todas as palavras com número ímpar de 's ou que terminam em :
Ora, tentemos então verificar se algumas palavras são ou não aceites por este autómato:
-
tenhamos ; não deve ser aceite: não tem número ímpar de 's nem termina em . As imagens abaixo procuram seguir a sequência de estados da palavra. Nenhuma delas termina num estado final, pelo que a palavra não é aceite (como esperado).
-
por outro lado, consideremos - a palavra deve ser aceite, já que tem número ímpar de 's. Vejamos então o caminho percorrido ao ler a palavra:
Como último passo, lemos aqui o símbolo vazio - podemos sempre lê-lo, e transitamos assim para o estado final desejado!
A linguagem reconhecida por um AFND é o conjunto das palavras aceites por esse AFND, isto é, o conjunto .
Exemplo de Linguagem Reconhecida por um AFND
Por exemplo, a linguagem reconhecida pelo AFND acima é a linguagem das palavras sobre o alfabeto que têm um número ímpares de 's ou acabam em .
Conversão de AFND's em AFD's
Temos que todas as linguagens reconhecidas por AFND's são linguagens regulares. Por outras palavras, seja qual for o AFND, existe um AFD que reconhece a mesma linguagem que o AFND de partida. Vamos ver daqui para a frente como obter a partir de um AFND o seu AFD equivalente.
Primeiro, começamos por remover os movimentos-:
Dado um AFND , temos que o AFND é-lhe equivalente se
- ;
- é tal que para cada e .
Pode-se entender a função como sendo aplicada a palavras com apenas uma letra.
Desta forma, é claro que (em , considerar os fechos- não tem efeito) pelo que os autómatos e reconhecem a mesma linguagem.
Remoção de movimentos-
Vamos compreender as alterações acima:
- Se podermos alcançar um estado final através de um movimento-, então se considerarmos esse estado como sendo também final, as palavras reconhecidas pelo nosso AFND não mudam;
- Para cada estado vamos ver que estados conseguimos alcançar usando apenas a letra . O conjunto de estados que conseguimos alcançar só com corresponde ao resultado de aplicar a todos os estados em e depois tirar o fecho- do resultado. Isto é, pego em todos os estados a que consigo chegar com , vejo onde consigo chegar com , e finalmente aplico outra vez.
Por exemplo, na imagem acima, estão assinalados que podem ser obtidos a partir de com a letra . Todas as transições a partir de passam por:
- ver onde é possível chegar com movimentos-. Na nossa imagem, estes primeiros movimentos estão assinalados a verde. O estado , apesar de estar em não está marcado com seta verde pois não há nenhum transição a partir de com .
- depois, fazemos então as transições que usam a letra , transições estas que estão assinaladas com setas vermelhas.
- finalmente, podemos ainda fazer mais movimentos-, que estão assinalados a azul.
Os estados que conseguimos obter a partir de com apenas um são os vermelhos e os azuis - .
Prova da equivalência entre e
A definição de função de transição estendida para o AFND será
em que em cima consideramos o fecho- de em , mas em baixo consideramos o fecho- de em .
Ou seja, a função de transição estendida de é tal que, dada uma letra e um estado , podemos alcançar todos os estados que podíamos em com essa letra (e possivelmente com movimentos-).
Indutivamente, se dermos uma palavra a , obtemos o mesmo conjunto de estados que obtiamos em .
Consequentemente, uma palavra é aceite em se e só se for aceite em .
Cuidado! Esta prova é indutiva com caso base , pelo que só funciona para palavras com pelo menos uma letra.
Isto é, falta provar a equivalência para .
Ora, é aceite em se e só se .
Ora, isto equivale a ser um estado final em , pelo que também é aceite em se e só se for aceite em .
Exemplo da remoção de movimentos-
Em relação à imagem mostrada acima, vamos construir um AFND equivalente sem movimentos- que vamos denominar .
Começamos por ver que a partir de podemos:
- usando chegar a (a partir de ),