Преобразование регулярных выражений в (минимальный) NFA, который принимает тот же язык, легко с помощью стандартных алгоритмов, например , алгоритма Томпсона . Другое направление кажется более утомительным, и иногда получающиеся выражения являются грязными.
Какие существуют алгоритмы для преобразования NFA в эквивалентные регулярные выражения? Есть ли преимущества в отношении сложности времени или размера результата?
Это должен быть справочный вопрос. Пожалуйста, включите общее описание вашего метода, а также нетривиальный пример.
Ответы:
Есть несколько методов для преобразования конечных автоматов в регулярные выражения. Здесь я опишу тот, который обычно преподается в школе, который очень нагляден. Я считаю, что это наиболее используемый на практике. Однако написание алгоритма не такая хорошая идея.
Метод удаления состояния
Этот алгоритм предназначен для обработки графа автомата и поэтому не очень подходит для алгоритмов, так как ему нужны графовые примитивы, такие как ... удаление состояний. Я опишу это с помощью примитивов более высокого уровня.
Ключевая идея
Идея состоит в том, чтобы рассмотреть регулярные выражения на ребрах, а затем удалить промежуточные состояния, сохраняя метки ребер согласованными.
Основную закономерность можно увидеть на следующих рисунках. Первый имеет метки между которые являются регулярными выражениями и мы хотим удалить .e , f , g , h , i qp,q,r e,f,g,h,i q
После удаления мы составляем вместе (сохраняя остальные ребра между и но это не отображается на этом):p re,f,g,h,i p r
пример
Используя тот же пример, что и в ответе Рафаэля :
мы последовательно удаляем :q2
а затем :q3
тогда мы все еще должны применить звезду к выражению от до . В этом случае конечное состояние также является начальным, поэтому нам просто нужно добавить звездочку:q 1q1 q1
Алгоритм
L[i,j]
это регулярное выражение языка от до . Сначала мы удалим все мульти-ребра:q jТеперь состояние снятия. Предположим, мы хотим удалить состояние :qk
Обратите внимание , что оба с карандашом бумаги и с помощью алгоритма вы должны упростить выражения , как∅ ε q k q j q kqi qk qj qk
star(ε)=ε
,e.ε=e
,∅+e=e
,∅.e=∅
(Под рукой вы просто не написать край , когда он не , или даже для самостоятельного цикла и вы игнорируете , когда есть нет перехода между и или и )Теперь, как использовать
remove(k)
? Вы не должны легко удалять окончательные или начальные состояния, иначе вы пропустите части языка.Если у вас есть только одно конечное состояние и одно начальное состояние тогда конечное выражение будет:q sqf qs
Если у вас есть несколько конечных состояний (или даже начальных состояний), то нет простого способа их объединения, кроме применения метода транзитивного замыкания. Обычно это не проблема вручную, но это неудобно при написании алгоритма. Гораздо более простой обходной путь - перечислить все пары и запустить алгоритм на графе (уже удаленном из состояния), чтобы получить все выражения предполагая, что является единственным начальным состоянием, а является единственным конечным состояние, то делает объединение всех .е с , е ы е д ы , е(s,f) es,f s f es,f
Это и тот факт, что это модифицирует языки более динамично, чем первый метод, делает его более подверженным ошибкам при программировании. Я предлагаю использовать любой другой метод.
Cons
В этом алгоритме много случаев, например, для выбора узла, который мы должны удалить, количества конечных состояний в конце, факта, что конечное состояние может быть начальным, и т. Д.
Обратите внимание, что теперь, когда алгоритм написан, это очень похоже на метод транзитивного замыкания. Только контекст использования отличается. Я не рекомендую реализовывать алгоритм, но использование метода для этого вручную - хорошая идея.
источник
ab
метод
Самый хороший метод, который я видел, это тот, который выражает автомат как систему уравнений (регулярных) языков, которые могут быть решены. Это особенно приятно, поскольку, кажется, дает более сжатые выражения, чем другие методы.
Пусть NFA без -переходов. Для каждого состояния создайте уравнениеε q iA=(Q,Σ,δ,q0,F) ε qi
где - множество конечных состояний, а означает, что существует переход от к помеченный . Если вы читаете как или (в зависимости от определения вашего регулярного выражения), вы видите, что это уравнение регулярных выражений.F qi→aqj qi qj a ∪ + ∣
Для решения системы вам нужны ассоциативность и дистрибутивность и (конкатенация строк), коммутативность и лемма Ардена ¹:∪ ⋅ ∪
Решением является набор регулярных выражений , по одному на каждое состояние . описывает именно те слова, которые могут быть приняты при запуске в ; поэтому (если - начальное состояние) является искомым выражением.Qi qi Qi A qi Q0 q0
пример
Для ясности мы обозначим одноэлементные множества по их элементу, то есть . Пример из-за Георга Зецше.a={a}
Рассмотрим это NFA:
[ источник ]
Соответствующая система уравнений:
Теперь вставьте третье уравнение во второе:
Для последнего шага мы применим лемму Ардена с , и . Обратите внимание, что все три языка являются регулярными и , что позволяет нам применять лемму. Теперь мы включим этот результат в первое уравнение:L=Q1 U=ab V=(b∪aa)⋅Q0 ε∉U={ab}
Таким образом, мы нашли регулярное выражение для языка, принятого вышеупомянутым автоматом, а именно
Обратите внимание, что он довольно лаконичен (сравните с результатами других методов), но не определяется однозначно; Решение системы уравнений с другой последовательностью манипуляций приводит к другому - эквивалентно! - выражения.
источник
maybe_union/2
предикат может использовать больше работы (особенно если исключить общий префикс) для создания более регулярных регулярных выражений. Другой способ увидеть этот метод состоит в том, чтобы понимать его как перевод с регулярного выражения в прямолинейную грамматику, где языки с Prolog-подобным объединением или ML-подобным сопоставлением с образцом создают очень хорошие преобразователи, так что это не только перо и бумага алгоритм :)Алгебраический метод Бжозовского
Это тот же метод, который описан в ответе Рафаэля , но с точки зрения систематического алгоритма, а затем и самого алгоритма. Оказывается, это легко и естественно реализовать, если вы знаете, с чего начать. Также это может быть проще, если рисовать все автоматы по какой-то причине нецелесообразно.
При написании алгоритма вы должны помнить, что уравнения должны быть всегда линейными, чтобы у вас было хорошее абстрактное представление уравнений, вещь, которую вы можете забыть, когда решаете вручную.
Идея алгоритма
Я не буду описывать, как это работает, поскольку это хорошо сделано в ответе Рафаэля, который я предлагаю прочитать раньше. Вместо этого я сосредотачиваюсь на том, в каком порядке вы должны решать уравнения, не делая слишком много дополнительных вычислений или дополнительных случаев.
Исходя из оригинального решения правила Ардена к уравнению языка мы можем рассматривать автомат как систему уравнений вида:X=A∗B X=AX∪B
мы можем решить это путем наведения на , обновив массивы и соответственно. На шаге имеем:n Ai,j Bi,j n
и правило Ардена дает нам:
и установив и мы получим:B′n=A∗n,nBn A′n,i=A∗n,nAn,i
и тогда мы можем удалить все потребности в системе, установив для :Xn i,j<n
Когда мы решили когда , мы получили уравнение, подобное этому:Xn n=1
без . Таким образом, мы получили наше регулярное выражение.A′1,i
Алгоритм
Благодаря этому мы можем построить алгоритм. Чтобы иметь то же соглашение, что и в приведенной выше индукции, мы будем говорить, что начальное состояние - а число состояний - . Во-первых, инициализация для заполнения :q1 m B
и :A
а затем решение:
окончательное выражение тогда:
Реализация
Даже если это может показаться системой уравнений, которая кажется слишком символичной для алгоритма, она хорошо подходит для реализации.
Вот реализация этого алгоритма в Ocaml(неработающая ссылка) . Обратите внимание, что, кроме функцииbrzozowski
, все для печати или для примера Рафаэля. Обратите внимание, что есть удивительно эффективная функция упрощения регулярных выраженийsimple_re
.источник
Метод транзитивного закрытия
Этот метод легко написать в форме алгоритма, но он генерирует нелепо большие регулярные выражения и нецелесообразен, если вы делаете это вручную, в основном потому, что это слишком систематично. Это хорошее и простое решение для алгоритма.
Ключевая идея
Пусть представляет регулярное выражение для строк, идущих от к используя состояния . Пусть будет числом состояний автомата.Rki,j qi qj {q1,…,qk} n
Предположим, что вы уже знаете регулярное выражение от до без промежуточного состояния (кроме конечностей) для всех . Затем вы можете догадаться, как добавление другого состояния повлияет на новое регулярное выражение : оно изменяется только при наличии прямых переходов к и может быть выражено так:Ri,j qi qj qk i,j R′i,j qk
( является и является .)R Rk−1 R′ Rk
пример
Мы будем использовать тот же пример, что и в ответе Рафаэля . Во-первых, вы можете использовать только прямые переходы.
Вот первый шаг (обратите внимание, что сам цикл с меткой преобразовал бы первый в .a ε (ε+a)
На втором шаге мы можем использовать (который для нас переименован в , потому что уже используется для вышеуказанной цели). Посмотрим, как работает .q0 q1 R0 R1
От до : .q 2 R 1 2 , 2 = R 0 2 , 2 + R 0 2 , 1 R 0 1 , 1 ∗ R 0 1 , 2 = ε + b ε ∗ a = ε + b aq2 q2 R12,2=R02,2+R02,1R01,1∗R01,2=ε+bε∗a=ε+ba
Почему это? Это происходит потому, что переход от к с использованием только в качестве промежуточного состояния можно выполнить, оставаясь здесь ( ) или переходя к ( ), повторяя там ( ) и возвращаясь ( ).q2 q2 q1 ε q1 a ε∗ b
Вы можете также вычислить и , и даст вам окончательное выражение, потому что является как начальным, так и конечным. Обратите внимание, что здесь было сделано много упрощений выражений. В противном случае первый из был бы а первый из был бы .R 3 R 3 1 , 1 1 a R 0 ( ∅ + a ) a R 1 ( ( ∅ + a ) + ε ( ε ) ∗ a )R2 R3 R31,1 1 a R0 (∅+a) a R1 ((∅+a)+ε(ε)∗a)
Алгоритм
Инициализация:
Транзитивное закрытие:
Тогда конечное выражение (предположим, что является начальным состоянием):qs
Но вы можете себе представить, что он генерирует некрасивые регулярные выражения. Вы действительно можете ожидать такие вещи, как который представляет тот же язык, что и . Обратите внимание, что упрощение регулярного выражения полезно на практике.a a(∅)∗+(a+(∅)∗)(ε)∗(a+∅) aa
источник