正規表現を同じ言語を受け入れる(最小限の)NFAに変換することは、Thompsonのアルゴリズムなどの標準アルゴリズムを使用すると簡単です。しかし、他の方向はより面倒であるように思われ、結果の表現が面倒な場合があります。
NFAを同等の正規表現に変換するためのアルゴリズムは何ですか?時間の複雑さや結果のサイズに関して利点はありますか?
これは参考質問になるはずです。メソッドの一般的な説明と重要な例を含めてください。
正規表現を同じ言語を受け入れる(最小限の)NFAに変換することは、Thompsonのアルゴリズムなどの標準アルゴリズムを使用すると簡単です。しかし、他の方向はより面倒であるように思われ、結果の表現が面倒な場合があります。
NFAを同等の正規表現に変換するためのアルゴリズムは何ですか?時間の複雑さや結果のサイズに関して利点はありますか?
これは参考質問になるはずです。メソッドの一般的な説明と重要な例を含めてください。
回答:
有限オートマトンから正規表現への変換を行う方法はいくつかあります。ここでは、学校で通常教えられる非常に視覚的なものについて説明します。実際に最も使用されていると思います。ただし、アルゴリズムを作成することはあまり良い考えではありません。
このアルゴリズムは、オートマトンのグラフの処理に関するものであり、...状態の削除などのグラフプリミティブが必要なため、アルゴリズムにはあまり適していません。高レベルのプリミティブを使用して説明します。
アイデアは、エッジの正規表現を考慮し、エッジラベルの一貫性を保ちながら中間状態を削除することです。
主なパターンは、以下の図で見ることができます。最初は間のラベルがある正規表現です、我々は削除する。e 、f 、g 、h 、i q
削除したら、します(と間の他のエッジは保持しますが、これには表示されません)。p r
ラファエルの答えと同じ例を使用して:
を連続して削除し。
そして:
その後、我々はまだからの発現にスターを適用する必要がに。この場合、最終状態も初期であるため、スターを追加するだけです。q 1
L[i,j]
からまでの言語の正規表現です。まず、すべてのマルチエッジを削除します。q j
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
さて、状態の削除。状態を削除するとします。
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 j個のq Kq k
さて、使い方はremove(k)
?最終状態または初期状態を軽く削除しないでください。削除すると、言語の一部が失われます。
for i = 1 to n:
if not(final(i)) and not(initial(i)):
remove(i)
最終状態と初期状態 1つしかない場合、最終式は次のようになります。q s
e := star(L[s,s]) . L[s,f] . star(L[f,s] . star(L[s,s]) . L[s,f] + L[f,f])
複数の最終状態(または初期状態)がある場合、推移閉包法を適用する以外に、これらの状態をマージする簡単な方法はありません。通常、これは手作業での問題ではありませんが、アルゴリズムを作成する際に厄介です。より簡単な回避策は、すべてのペアを列挙し、(既に状態が削除された)グラフでアルゴリズムを実行して、が唯一の初期状態であり、が唯一の最終状態であると仮定してすべての式を取得することです状態にしてから、すべての結合を行います。e s 、f s f e s 、f
これと、これが最初の方法よりも動的に言語を変更しているという事実により、プログラミング時にエラーが発生しやすくなります。他の方法を使用することをお勧めします。
このアルゴリズムには多くのケースがあります。たとえば、削除するノードの選択、最後の最終状態の数、最終状態も初期状態にできるという事実などです。
アルゴリズムが作成されたので、これは推移閉包法によく似ていることに注意してください。使用法のコンテキストのみが異なります。アルゴリズムを実装することはお勧めしませんが、メソッドを使用して手動で実行することをお勧めします。
ab
私が見た中で最も素晴らしい方法は、解決できる(通常の)言語の方程式システムとしてオートマトンを表現するものです。他のメソッドよりも簡潔な式を生成するように見えるため、特に便利です。
を -transitions なしのNFAとしましょう。すべての状態について、方程式を作成しますεのq個のI
ここで、は最終状態のセットであり、は、aでラベル付けされたからへの遷移があることを意味ます。をまたは(正規表現の定義に応じて)と読むと、これは正規表現の方程式であることがわかります。
システムを解決するために、あなたは、結合性との分配性必要と(文字列連結)の可換性とアーデンの補題が ¹を:
通常の言語をとしましょう。その後、
解決策は、状態ごとに1つずつある正規表現セットです。は、開始されときにが受け入れることができる単語を正確に記述します。したがって、(が初期状態の場合)が望ましい式です。
わかりやすくするために、シングルトンセットをその要素、つまり表します。この例は、Georg Zetzscheによるものです。
このNFAを検討してください。
[ ソース ]
対応する方程式系は次のとおりです。
次に、3番目の方程式を2番目の方程式に差し込みます。
最後のステップでは、、およびアーデンの補題を適用します。3つの言語はすべて正規であり、であり、補助定理を適用できることに注意してください。次に、この結果を最初の方程式に代入します。
したがって、上記のオートマトンで受け入れられる言語の正規表現、つまり、
他のメソッドの結果と比較して非常に簡潔ですが、一意に決定されていないことに注意してください。異なる一連の操作で方程式システムを解くことは、他の同等の結果につながります!-式。
maybe_union/2
述語が整然と正規表現を作るために、より多くの作業(。共通の接頭辞を排除ESP WRT)を使用することができます。この方法を見るもう1つの方法は、正規表現から右線形文法への翻訳として理解することです。Prologのような統一またはMLのようなパターンマッチングを行う言語は、非常に優れたトランスデューサーになるため、ペンと紙だけではありませんアルゴリズム:)
これは、Raphaelの答えで説明されているものと同じ方法ですが、体系的なアルゴリズムの観点から、そして実際にはアルゴリズムの観点からです。どこから始めればよいかがわかれば、実装が簡単で自然であることがわかります。また、何らかの理由ですべてのオートマトンを描画するのが実用的でない場合は、手作業で簡単にすることもできます。
アルゴリズムを書くときは、方程式を常に線形にする必要があることを覚えておく必要があります。これにより、方程式を手作業で解くときに忘れることができる、方程式の優れた抽象的な表現が得られます。
それがどのように機能するかについては説明しません。これは、前に読むことをお勧めするラファエルの答えでうまく行われているからです。代わりに、余分な計算や余分なケースをあまり行わずに方程式を解く順序に焦点を当てます。
開始アーデンのルールの独創的なソリューション言語式に我々は、フォームの方程式のセットとして、オートマトンを考えることができます。
配列および適宜更新することにより、帰納法によりこれを解決できます。ステップには、次のものがあります。
そして、アーデンの規則は私たちに与えます:
そして、およびを設定すると、次のようになります。
そして、設定することにより、システム内のすべてのニーズを削除できます。
ときにを解くと、次のような方程式が得られます。
ないと。したがって、正規表現を取得しました。
これにより、アルゴリズムを構築できます。上記の帰納法と同じ規則を持たせるために、初期状態はあり、状態の数はと言います。まず、を埋めるための初期化:
for i = 1 to m:
if final(i):
B[i] := ε
else:
B[i] := ∅
および:
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] := ∅
そして、解決:
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]
最終的な式は次のとおりです。
e := B[1]
アルゴリズムにとって象徴的すぎると思われる連立方程式のように思われる場合でも、これは実装に適しています。Ocaml(リンク切れ)でのこのアルゴリズムの実装を次に示します。関数以外はbrzozowski
、すべて印刷するか、Raphaelの例で使用することに注意してください。正規表現を単純化する驚くほど効率的な機能があることに注意してくださいsimple_re
。
この方法はアルゴリズムの形式で簡単に記述できますが、非常に大きな正規表現を生成し、手作業で行うと非実用的です。これは主に体系的すぎるためです。ただし、これはアルゴリズムの優れたシンプルなソリューションです。
ましょから行くの文字列の正規表現を表すする状態を使用して。してみましょうオートマトンの状態数とします。
すべてのに対して、からへの正規表現が既にわかっており、中間状態(四肢を除く)がないとします。次に、別の状態の追加が新しい正規表現どのように影響するかを推測できますへの直接遷移がある場合にのみ変化し、次のように表現できます。 q i q j
(は、はです。)R k − 1 R ′ R k
ラファエルの答えと同じ例を使用します。最初は、直接遷移のみを使用できます。
これが最初のステップです(ラベルを持つ自己ループは、最初のを変換ことに注意してください。ε(ε + A )
2番目のステップでは、を使用できます(は上記の目的ですでに使用されているため、に名前が変更されています)。動作を確認します。
に:。
何故ですか?行くからですするのみ使用中間状態はここに滞在することにより行うことができるよう()、またはしようとして()、そこループ()及び(戻ってくる)。
あなたは次のように計算することができると、あまりにも、そしてので、あなたの最終的な式を与えるだろう最初と最後の両方です。ここでは、式の簡略化が数多く行われていることに注意してください。そうでなければ最初のであろう及び第一のあろう。R 3 R 3 1 、1 1 R 0(∅ + A )R 1((∅ + A )+ ε(ε )* A )
初期化:
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
推移閉包:
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)
次に、最終式はです(が初期状態であると仮定):
e := ∅
for i = 1 to n:
if final(i):
e := e + R[s,i,n]
しかし、それはregularい正規表現を生成すると想像できます。実際、と同じ言語を表すようなものを期待できます。正規表現を単純化することは、実際には役立つことに注意してください。a a