Como converter autômatos finitos em expressões regulares?


115

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.


2
Observe uma pergunta semelhante em cstheory.SE, que provavelmente não é adequada para o nosso público.
Raphael

todas as respostas usam a técnica formal para escrever RE do DFA. Acredito que minha técnica de análise é comparativamente fácil e objetiva que demonstro em minha resposta: Qual é a linguagem desse autômato finito determinístico? Eu acho que seria útil algum dia. Sim, de curso em algum momento eu me usa método formal (Arden teorema) para escrever RE é questão é complexa como dada neste exemplo: Como escrever expressão regular para uma DFA
Grijesh Chauhan

Respostas:


94

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,re,f,g,h,iq

autômato pqr

Uma vez removidos, compomos juntos (enquanto preservamos as outras arestas entre e mas isso não é exibido aqui):p re,f,g,h,ipr

insira a descrição da imagem aqui

Exemplo

Usando o mesmo exemplo da resposta de Raphael :

1-2-3 autômato

removemos sucessivamente :q2

1-3 autômato

e depois :q3

1 autômato

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 1q1q1

(ab+(b+aa)(ba)(a+bb))

Algoritmo

L[i,j]é a regexp do idioma de a . Primeiro, removemos todas as arestas múltiplas:q jqiqj

for i = 1 to n:
  for j = 1 to n:
    if i == j then:
      L[i,j] := ε
    else:
      L[i,j] := ∅
    for a in Σ:
      if trans(i, a, j):
        L[i,j] := L[i,j] + a

Agora, a remoção do estado. Suponha que desejemos remover o estado :qk

remove(k):
  for i = 1 to n:
    for j = 1 to n:
      L[i,i] += L[i,k] . star(L[k,k]) . L[k,i]
      L[j,j] += L[j,k] . star(L[k,k]) . L[k,j]
      L[i,j] += L[i,k] . star(L[k,k]) . L[k,j]
      L[j,i] += L[j,k] . star(L[k,k]) . L[k,i]

Note-se que ambos com um lápis de papel e com um algoritmo que você deve simplificar expressões como 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 )q i q k q j q kεqiqkqjqk

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.

for i = 1 to n:
  if not(final(i)) and not(initial(i)):
    remove(i)

Se você tiver apenas um estado final e um estado inicial , a expressão final será:q sqfqs

e := star(L[s,s]) . L[s,f] . star(L[f,s] . star(L[s,s]) . L[s,f] + L[f,f])

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,fsfes,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.


1
No exemplo, 2ª imagem, após a remoção do nó "2", há uma borda ausente - borda do loop (ab) no nó A.
Panos Kal.

@Kabamaru: Fixed. Mas agora acho que o na 3ª imagem também deveria estar , e da mesma forma talvez na expressão regular final. εab
Wandering Logic

Você pode fazer o algoritmo funcionar para qualquer número de estados inicial e final adicionando um novo inicial e um novo estado final e conectando-os aos estados inicial e final originais por -edges. Agora remova todos os estados originais. A expressão é então encontrada na única borda restante de a . A construção não dará loops em ou pois esses estados não têm resp. bordas de saída. Ou, se você for rigoroso, eles terão rótulos representando o conjunto vazio. q - ε q + q - q + q -q+qεq+qq+q
Hendrik Jan

1
Ainda existe um problema com o segundo exemplo: antes da simplificação, os autômatos aceitam "ba", (1, 3, 1), mas após a simplificação, não.
Wvxvw

50

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

Qi=qiaqjaQj{{ε}, qiF, else

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.Fqiaqjqiqja+

Para resolver o sistema, você precisa de associatividade e distributividade de e (concatenação de cordas), comutatividade de e Lemma de Arden ¹:

Deixe- linguagens regulares com . Então,L,U,VΣεU

L=ULVL=UV

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.QiqiQiAqiQ0q0


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:

exemplo nfa
[ fonte ]

O sistema de equações correspondente é:

Q0=aQ1bQ2εQ1=bQ0aQ2Q2=aQ0bQ1

Agora conecte a terceira equação na segunda:

Q1=bQ0a(aQ0bQ1)=abQ1(baa)Q0=(ab)(baa)Q0

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=Q1U=abV=(baa)Q0εU={ab}

Q0=a(ab)(baa)Q0baQ0bb(ab)(baa)Q0ε=((abb)(ab)(baa)ba)Q0ε=((abb)(ab)(baa)ba)(by Arden's Lemma)

Assim, encontramos uma expressão regular para o idioma aceito pelo autômato acima, a saber

((a+bb)(ab)(b+aa)+ba).

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.


  1. Para uma prova do Lema de Arden, veja aqui .

1
Qual é a complexidade de tempo desse algoritmo? Existe um limite no tamanho da expressão produzida?
jmite

@ jmite: Não faço ideia. Acho que não tentaria implementar isso (outros métodos parecem mais viáveis ​​nesse sentido), mas usá-lo como um método de caneta e papel.
Raphael

1
Aqui está uma implementação do Prolog desse algoritmo: github.com/wvxvw/intro-to-automata-theory/blob/master/automata/…, mas seu maybe_union/2predicado 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 :)
wvxvw 23/09/15

Apenas uma pergunta. O ε na primeira equação é porque Qo é um estado inicial ou porque é um estado final? Da mesma forma, se eu tiver dois estados finais se aplica?
Georgio3

Qiq0

28

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.

X=ABX=AXB

Xi=Bi+Ai,1X1++Ai,nXn

nAi,jBi,jn

Xn=Bn+An,1X1++An,nXn

e o governo de Arden nos dá:

Xn=An,n(Bn+An,1X1++An,n1Xn1)

Bn=An,nBnAn,i=An,nAn,i

Xn=Bn+An,1X1++An,n1Xn1

Xni,j<n

Bi=Bi+Ai,nBn
Ai,j=Ai,j+Ai,nAn,j

Xnn=1

X1=B1

A1,i

O algoritmo

q1mB

for i = 1 to m:
  if final(i):
    B[i] := ε
  else:
    B[i] := ∅

A

for i = 1 to m:
  for j = 1 to m:
    for a in Σ:
      if trans(i, a, j):
        A[i,j] := a
      else:
        A[i,j] := ∅

e então a solução:

for n = m decreasing to 1:
  B[n] := star(A[n,n]) . B[n]
  for j = 1 to n:
    A[n,j] := star(A[n,n]) . A[n,j];
  for i = 1 to n:
    B[i] += A[i,n] . B[n]
    for j = 1 to n:
      A[i,j] += A[i,n] . A[n,j]

a expressão final é então:

e := B[1]

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ção brzozowski, tudo é para imprimir ou usar no exemplo de Raphael. Observe que há uma função surpreendentemente eficiente de simplificação de expressões regulares simple_re.


4
Link is dead ...
Columbo


24

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

Ri,jkqiqj{q1,,qk}n

Ri,jqiqjqki,jRi,jqk

Ri,j=Ri,j+Ri,k.Rk,k.Rk,j

RRk1RRk

Exemplo

Usaremos o mesmo exemplo da resposta de Rafael . No início, você pode usar apenas as transições diretas.

aε(ε+a)

R0=[εabbεaabε]

q0q1R0R1

q2q2R2,21=R2,20+R2,10R1,10R1,20=ε+bεa=ε+ba

q2q2q1εq1aεb

R1=[εabbε+baa+bbab+aaε+ab]

R2R3R1,131aR0(+a)aR1((+a)+ε(ε)a)

Algoritmo

Inicialização:

for i = 1 to n:
  for j = 1 to n:
    if i == j:
      R[i,j,0] := ε
    else:
      R[i,j,0] := ∅
    for a in Σ:
      if trans(i, a, j):
        R[i,j,0] := R[i,j,0] + a

Fechamento transitivo:

for k = 1 to n:
  for i = 1 to n:
    for j = 1 to n:
      R[i,j,k] := R[i,j,k-1] + R[i,k,k-1] . star(R[k,k,k-1]) . R(k,j,k-1)

qs

e := ∅
for i = 1 to n:
  if final(i):
    e := e + R[s,i,n]

()+(a+())(ε)(a+)aa

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.