Есть несколько методов для преобразования конечных автоматов в регулярные выражения. Здесь я опишу тот, который обычно преподается в школе, который очень нагляден. Я считаю, что это наиболее используемый на практике. Однако написание алгоритма не такая хорошая идея.
Метод удаления состояния
Этот алгоритм предназначен для обработки графа автомата и поэтому не очень подходит для алгоритмов, так как ему нужны графовые примитивы, такие как ... удаление состояний. Я опишу это с помощью примитивов более высокого уровня.
Ключевая идея
Идея состоит в том, чтобы рассмотреть регулярные выражения на ребрах, а затем удалить промежуточные состояния, сохраняя метки ребер согласованными.
Основную закономерность можно увидеть на следующих рисунках. Первый имеет метки между которые являются регулярными выражениями и мы хотим удалить .e , f , g , h , i qp,q,re,f,g,h,iq
После удаления мы составляем вместе (сохраняя остальные ребра между и но это не отображается на этом):p re,f,g,h,ipr
пример
Используя тот же пример, что и в ответе Рафаэля :
мы последовательно удаляем :q2
а затем :q3
тогда мы все еще должны применить звезду к выражению от до . В этом случае конечное состояние также является начальным, поэтому нам просто нужно добавить звездочку:q 1q1q1
(ab+(b+aa)(ba)∗(a+bb))∗
Алгоритм
L[i,j]
это регулярное выражение языка от до . Сначала мы удалим все мульти-ребра: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
Теперь состояние снятия. Предположим, мы хотим удалить состояние :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]
Обратите внимание , что оба с карандашом бумаги и с помощью алгоритма вы должны упростить выражения , как star(ε)=ε
, e.ε=e
, ∅+e=e
, ∅.e=∅
(Под рукой вы просто не написать край , когда он не , или даже для самостоятельного цикла и вы игнорируете , когда есть нет перехода между и или и )∅εq k q j q kqiqkqjqk
Теперь, как использовать remove(k)
? Вы не должны легко удалять окончательные или начальные состояния, иначе вы пропустите части языка.
for i = 1 to n:
if not(final(i)) and not(initial(i)):
remove(i)
Если у вас есть только одно конечное состояние и одно начальное состояние тогда конечное выражение будет:q sqfqs
e := star(L[s,s]) . L[s,f] . star(L[f,s] . star(L[s,s]) . L[s,f] + L[f,f])
Если у вас есть несколько конечных состояний (или даже начальных состояний), то нет простого способа их объединения, кроме применения метода транзитивного замыкания. Обычно это не проблема вручную, но это неудобно при написании алгоритма. Гораздо более простой обходной путь - перечислить все пары и запустить алгоритм на графе (уже удаленном из состояния), чтобы получить все выражения предполагая, что является единственным начальным состоянием, а является единственным конечным состояние, то делает объединение всех .е с , е ы е д ы , е(s,f)es,fsfes,f
Это и тот факт, что это модифицирует языки более динамично, чем первый метод, делает его более подверженным ошибкам при программировании. Я предлагаю использовать любой другой метод.
Cons
В этом алгоритме много случаев, например, для выбора узла, который мы должны удалить, количества конечных состояний в конце, факта, что конечное состояние может быть начальным, и т. Д.
Обратите внимание, что теперь, когда алгоритм написан, это очень похоже на метод транзитивного замыкания. Только контекст использования отличается. Я не рекомендую реализовывать алгоритм, но использование метода для этого вручную - хорошая идея.