DFAの最小化について質問があります。そのため、私は非常によく知られた手法を使用して正規表現をNFAに変換し、goto/closureアルゴリズムを使用してそれからDFAを構築しました。ここで問題は、どうすればそれを最小化できるかということです。私はここでそれについての講義を見てきました:http ://www.youtube.com/watch?v = T9Z66NF5YRk 、そして私はまだポイントを得ることができません。DFA最小化とは何ですか?これは、IDENTICAL状態(同じ文字で同じ状態になる状態)をマージするだけですか、それとも別の状態ですか?
それで、私は次の文法から始めました:
%digit = '0'..'9'
%letter = 'a'..'z' | 'A'..'Z'
%exponent = ("e" | "E") ("+" | "-")? digit+
T_INT = digit+
T_FLOAT = T_INT exponent
T_IDENTIFIER = (letter | "$" | "_") (letter | "$" | "_" | digit)*
最終的に次のDFA(JSONとして表される)になります。
{
"START": [{
"type": "range",
"from": 36,
"to": 36,
"shift": "1"
}, {
"type": "range",
"from": 48,
"to": 57,
"shift": "2"
}, {
"type": "range",
"from": 65,
"to": 90,
"shift": "1"
}, {
"type": "range",
"from": 95,
"to": 95,
"shift": "1"
}, {
"type": "range",
"from": 97,
"to": 122,
"shift": "1"
}],
"1": [{
"type": "range",
"from": 36,
"to": 36,
"shift": "1"
}, {
"type": "range",
"from": 48,
"to": 57,
"shift": "1"
}, {
"type": "range",
"from": 65,
"to": 90,
"shift": "1"
}, {
"type": "range",
"from": 95,
"to": 95,
"shift": "1"
}, {
"type": "range",
"from": 97,
"to": 122,
"shift": "1"
}, {
"shift": ["t_identifier"]
}],
"2": [{
"type": "range",
"from": 48,
"to": 57,
"shift": "2"
}, {
"type": "range",
"from": 69,
"to": 69,
"shift": "3"
}, {
"type": "range",
"from": 101,
"to": 101,
"shift": "3"
}, {
"shift": ["t_int"]
}],
"3": [{
"type": "range",
"from": 43,
"to": 43,
"shift": "5"
}, {
"type": "range",
"from": 45,
"to": 45,
"shift": "5"
}, {
"type": "range",
"from": 48,
"to": 57,
"shift": "4"
}],
"4": [{
"type": "range",
"from": 48,
"to": 57,
"shift": "4"
}, {
"shift": ["t_float"]
}],
"5": [{
"type": "range",
"from": 48,
"to": 57,
"shift": "4"
}]
}
では、どうすれば最小化できますか?
アップデート:
さて、これが私のアルゴリズムです。次のDFAが与えられます:
{
0: [{
from: 97,
to: 97,
shift: 1
}],
1: [{
from: 97,
to: 97,
shift: 3
}, {
from: 98,
to: 98,
shift: 2
}],
2: [{
from: 98,
to: 98,
shift: 4
}],
3: [{
from: 97,
to: 97,
shift: 3
}, {
from: 98,
to: 98,
shift: 4
}],
4: [{
from: 98,
to: 98,
shift: 4
}]
}
これは私がそれを最小化するために行うことです:
各状態(私の例では0、1、2、3、4として番号が付けられています)について、そのような状態を識別する一意のハッシュを取得します(たとえば、state0の場合、これはfrom = 97、to = 97、shift = 1、state1の場合はthis from = 97、to = 97、shift = 3&from = 98、to = 98、shift = 2などになります…)
得られたハッシュを比較し、2つの同一のハッシュが見つかった場合は、それをマージします。私の例では、state2ハッシュはfrom = 98&shift = 4&to = 98になり、state4ハッシュはfrom = 98&shift = 4&to = 98になります。これらは同じなので、新しいstate5にマージできます。その後、DFAはこんな風に見える:
{ 0: [{ from: 97, to: 97, shift: 1 }], 1: [{ from: 97, to: 97, shift: 3 }, { from: 98, to: 98, shift: 5 }], 3: [{ from: 97, to: 97, shift: 3 }, { from: 98, to: 98, shift: 5 }], 5: [{ from: 98, to: 98, shift: 5 }]
}
変更がなくなるまでこれを続けます。次のステップ(状態1と3をマージ)はDFAを次の形式に変換します。
{ 0: [{ from: 97, to: 97, shift: 6 }], 6: [{ from: 97, to: 97, shift: 6 }, { from: 98, to: 98, shift: 5 }], 5: [{ from: 98, to: 98, shift: 5 }]
}
同一の状態はもうありません。つまり、完了です。
2回目の更新:
さて、次の正規表現が与えられます:'a'('ce')*('d' |'xa' |'AFe')+ | 'b'('ce')*('d' |'xa' |'AFe')+'ce' +次のDFAがあります(START->開始状態、["accept"]-> so to受け入れ状態への移行と言います):
{
"START": [{
"type": "range",
"from": 98,
"to": 98,
"shift": "1.2"
}, {
"type": "range",
"from": 97,
"to": 97,
"shift": "17.18"
}],
"1.2": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "10"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "6.7"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "8"
}, {
"type": "range",
"from": 99,
"to": 99,
"shift": "4"
}],
"10": [{
"type": "range",
"from": 97,
"to": 97,
"shift": "6.7"
}],
"6.7": [{
"type": "range",
"from": 99,
"to": 99,
"shift": "15"
}, {
"type": "range",
"from": 120,
"to": 120,
"shift": "13"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "6.7"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "11"
}],
"15": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "14.accept"
}],
"14.accept": [{
"type": "range",
"from": 99,
"to": 99,
"shift": "16"
}, {
"shift": ["accept"]
}],
"16": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "14.accept"
}],
"13": [{
"type": "range",
"from": 97,
"to": 97,
"shift": "6.7"
}],
"11": [{
"type": "range",
"from": 70,
"to": 70,
"shift": "12"
}],
"12": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "6.7"
}],
"8": [{
"type": "range",
"from": 70,
"to": 70,
"shift": "9"
}],
"9": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "6.7"
}],
"4": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "2.3"
}],
"2.3": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "10"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "6.7"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "8"
}, {
"type": "range",
"from": 99,
"to": 99,
"shift": "5"
}],
"5": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "2.3"
}],
"17.18": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "25"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "22.accept"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "23"
}, {
"type": "range",
"from": 99,
"to": 99,
"shift": "20"
}],
"25": [{
"type": "range",
"from": 97,
"to": 97,
"shift": "22.accept"
}],
"22.accept": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "28"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "22.accept"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "26"
}, {
"shift": ["accept"]
}],
"28": [{
"type": "range",
"from": 97,
"to": 97,
"shift": "22.accept"
}],
"26": [{
"type": "range",
"from": 70,
"to": 70,
"shift": "27"
}],
"27": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "22.accept"
}],
"23": [{
"type": "range",
"from": 70,
"to": 70,
"shift": "24"
}],
"24": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "22.accept"
}],
"20": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "18.19"
}],
"18.19": [{
"type": "range",
"from": 120,
"to": 120,
"shift": "25"
}, {
"type": "range",
"from": 100,
"to": 100,
"shift": "22.accept"
}, {
"type": "range",
"from": 65,
"to": 65,
"shift": "23"
}, {
"type": "range",
"from": 99,
"to": 99,
"shift": "21"
}],
"21": [{
"type": "range",
"from": 101,
"to": 101,
"shift": "18.19"
}]
}
話は同じですが、どうすれば最小化できますか?古典的なHopcroftのアルゴリズムに従って、このすべてのテーブル構造を使用し、区別できない状態を判別し、それらをマージするなどの場合、15の状態を含むDFAを取得します(このツールを使用してください:http://regexvisualizer.apphb.com/この正規表現a(ce)(d | xa | AFe)+ | b(ce)(d | xa | AFe)+ ce +を使用して確認します)。Hopcroftのアルゴリズムを使用して縮小した後のDFAは次のようになります。
私が思いついたアルゴリズムは、Hopcroftのアルゴリズムを「再考」した後、上に表示されているものよりも小さいDFAを構築します(画質については申し訳ありませんが、なぜ小さいのかを理解するために段階的に再描画する必要がありました)。
そして、これがどのように機能するかです。「状態の同等性」に関する決定は、状態が与えられたハッシュ関数の結果に基づいています(たとえば、「START」)。その状態から開始すると、DFAから構築できる短い文字列が作成されます。 。上記のDFAとSTART状態が与えられると、次の文字列を作成できます:98-> 120、98-> 100、98-> 65、98-> 99、97-> 120、97-> 100、97-> 65 、97-> 99なので、START状態のハッシュ関数の結果とします。DFAの各状態に対してこの関数を実行すると、一部の状態でこの関数が同じ結果( "1.2"、 "6.7"、 "2.3" AND "10"、 "13" AND"15"を与えることがわかります。 、"16" AND "11"、 "8"、 "26"、 "23" AND "12"、 "9"、 "4"、
どこかで間違っていることがわかりますが、アルゴリズムによって生成された最小化されたDFAの何が問題になっているのかわかりませんか?