Converter expressões regulares em NFA (mínimo) que aceitam o mesmo idioma é fácil com algoritmos padrão, por exemplo, o algoritmo de Thompson . A outra direção parece ser mais entediante e, às vezes, as expressões resultantes são confusas.
Quais algoritmos existem para converter NFA em expressões regulares equivalentes? Existem vantagens em relação à complexidade do tempo ou tamanho do resultado?
Esta deveria ser uma pergunta de referência. Inclua uma descrição geral do seu método, bem como um exemplo não trivial.
Respostas:
Existem vários métodos para fazer a conversão de autômatos finitos em expressões regulares. Aqui vou descrever o que normalmente é ensinado na escola, que é muito visual. Eu acredito que é o mais usado na prática. No entanto, escrever o algoritmo não é uma boa idéia.
Método de remoção de estado
Esse algoritmo trata do manuseio do gráfico do autômato e, portanto, não é muito adequado para algoritmos, pois ele precisa de primitivas do gráfico, como ... remoção de estado. Vou descrevê-lo usando primitivas de nível superior.
A ideia chave
A idéia é considerar expressões regulares nas arestas e remover estados intermediários, mantendo os rótulos das arestas consistentes.
O padrão principal pode ser visto a seguir nas figuras. O primeiro possui rótulos entre que são expressões regulares queremos remover .e , f , g , h , i qp , q, r e , f, g, H , i q
Uma vez removidos, compomos juntos (enquanto preservamos as outras arestas entre e mas isso não é exibido aqui):p re , f, g, H , i p r
Exemplo
Usando o mesmo exemplo da resposta de Raphael :
removemos sucessivamente :q2
e depois :q3
ainda precisamos aplicar uma estrela na expressão de a . Nesse caso, o estado final também é inicial; portanto, precisamos apenas adicionar uma estrela:q 1q1 q1
Algoritmo
L[i,j]
é a regexp do idioma de a . Primeiro, removemos todas as arestas múltiplas:q jAgora, a remoção do estado. Suponha que desejemos remover o estado :qk
Note-se que ambos com um lápis de papel e com um algoritmo que você deve simplificar expressões como∅ q i q k q j q kε qEu qk qj qk
star(ε)=ε
,e.ε=e
,∅+e=e
,∅.e=∅
(por mão você simplesmente não escrever a borda quando não está , ou mesmo para uma auto-loop e você ignorar quando há nenhuma transição entre e ou e )Agora, como usar
remove(k)
? Você não deve remover os estados finais ou iniciais de ânimo leve, caso contrário, perderá partes do idioma.Se você tiver apenas um estado final e um estado inicial , a expressão final será:q sqf qs
Se você possui vários estados finais (ou mesmo estados iniciais), não há uma maneira simples de mesclar esses, além de aplicar o método de fechamento transitivo. Normalmente, este não é um problema manual, mas é complicado ao escrever o algoritmo. Uma solução muito mais simples é enumerar todos os pares e executar o algoritmo no gráfico (já removido pelo estado) para obter todas as expressões supondo que é o único estado inicial é o único final estado, fazendo a união de todos os .e s , f s f e s , f( s , f) es , f s f es , f
Isso e o fato de modificar linguagens de maneira mais dinâmica que o primeiro método tornam mais propenso a erros na programação. Eu sugiro usar qualquer outro método.
Contras
Existem muitos casos nesse algoritmo, por exemplo, para escolher qual nó devemos remover, o número de estados finais no final, o fato de que um estado final também pode ser inicial etc.
Observe que agora que o algoritmo está escrito, é muito parecido com o método de fechamento transitivo. Somente o contexto do uso é diferente. Não recomendo implementar o algoritmo, mas usar o método para fazer isso manualmente é uma boa idéia.
fonte
ab
Método
O método mais legal que eu já vi é o que expressa o autômato como sistema de equações de linguagens (regulares) que podem ser resolvidas. É particularmente agradável, pois parece produzir expressões mais concisas do que outros métodos.
Seja um NFA sem varepsilon. Para cada estado , crie a equaçãoA=(Q,Σ,δ,q0,F) ε qi
onde é o conjunto de estados finais e significa que há uma transição de para rotulado com . Se você ler como ou (dependendo da sua definição de expressão regular), verá que essa é uma equação de expressões regulares.F qi→aqj qi qj a ∪ + ∣
Para resolver o sistema, você precisa de associatividade e distributividade de e (concatenação de cordas), comutatividade de e Lemma de Arden ¹:∪ ⋅ ∪
A solução é um conjunto de expressões regulares , uma para cada estado . descreve exatamente aquelas palavras que podem ser aceitas por quando iniciadas em ; portanto, (se for o estado inicial) é a expressão desejada.Qi qi Qi A qi Q0 q0
Exemplo
Por uma questão de clareza, denotamos conjuntos de singleton por seu elemento, ou seja, . O exemplo é devido a Georg Zetzsche.a={a}
Considere este NFA:
[ fonte ]
O sistema de equações correspondente é:
Agora conecte a terceira equação na segunda:
Para a última etapa, aplicamos o lema de Arden com , e . Observe que todos os três idiomas são regulares e , permitindo aplicar o lema. Agora, conectamos esse resultado à primeira equação:L=Q1 U=ab V=(b∪aa)⋅Q0 ε∉U={ab}
Assim, encontramos uma expressão regular para o idioma aceito pelo autômato acima, a saber
Observe que é bastante sucinto (compare com o resultado de outros métodos), mas não é determinado exclusivamente; resolver o sistema de equações com uma sequência diferente de manipulações leva a outro equivalente! -- expressões.
fonte
maybe_union/2
predicado poderia usar mais trabalho (especialmente wrt, eliminando o prefixo comum) para criar expressões regulares mais organizadas. Outra maneira de ver esse método é entendê-lo como tradução de regex para gramática linear direita, onde idiomas com unificação semelhante a Prolog ou correspondência de padrão semelhante a ML criam ótimos transdutores, por isso não é apenas uma caneta e papel algoritmo :)Método algébrico de Brzozowski
Esse é o mesmo método descrito na resposta de Raphael , mas do ponto de vista de um algoritmo sistemático e, de fato, do algoritmo. É fácil e natural de implementar depois que você sabe por onde começar. Também pode ser mais fácil à mão se desenhar todos os autômatos for impraticável por algum motivo.
Ao escrever um algoritmo, lembre-se de que as equações devem ser sempre lineares para que você tenha uma boa representação abstrata das equações, algo que você pode esquecer quando estiver resolvendo manualmente.
A ideia do algoritmo
Não vou descrever como funciona, pois é bem feito na resposta de Raphael, que sugiro ler antes. Em vez disso, concentro-me em qual ordem você deve resolver as equações sem fazer muitos cálculos ou casos extras.
e o governo de Arden nos dá:
O algoritmo
e então a solução:
a expressão final é então:
Implementação
Mesmo que pareça um sistema de equações que pareça simbólico demais para um algoritmo, este é adequado para uma implementação.
Aqui está uma implementação desse algoritmo no Ocaml(link quebrado) . Observe que, além da funçãobrzozowski
, tudo é para imprimir ou usar no exemplo de Raphael. Observe que há uma função surpreendentemente eficiente de simplificação de expressões regularessimple_re
.fonte
Método de fechamento transitivo
Esse método é fácil de escrever na forma de um algoritmo, mas gera expressões regulares absurdamente grandes e é impraticável se você o fizer manualmente, principalmente porque isso é sistemático demais. É uma solução boa e simples para um algoritmo.
A ideia chave
Exemplo
Usaremos o mesmo exemplo da resposta de Rafael . No início, você pode usar apenas as transições diretas.
Algoritmo
Inicialização:
Fechamento transitivo:
fonte