✖

非決定性オートマトンのシミュレーション

非決定性オートマトンがあり、それがある語を受理するかどうかを調べたいとき、方法はいくつかあります。オートマトンを決定性オートマトンに変換してもよいし、何とかして試してみることもできます。また、NFA の動作をシミュレートするアルゴリズムを使うこともできます。

アルゴリズムの考え方

考え方を、例を使って具体的に見てみましょう。次のオートマトンを考えます。

これはある言語を受理する非決定性有限オートマトンです。どんな言語かは、今は気にしなくてかまいません。このオートマトンの、入力語11001に対する計算がどうなるかを見ていきます。考え方は次のとおりです。語から記号を1つ処理し、同じ記号に対する複数の遷移によって進める先をすべて調べます。これらの状態をすべて覚えておき、次のステップでは「これらの状態すべてに同時にいる」と考えて、そこからさらにどこへ進めるかを調べます。

そこで、今いる可能性のある状態の集合と、語のまだ読んでいない部分を覚えておきます。はじめは組 ⟨{q0}, 11001⟩ です。状態 q0(初期状態)から始め、入力は語11001です。オートマトンは次のようになります。今いる状態を赤の太線で強調しています。

最初の記号は1です。状態 q0 からは記号1の遷移が1本しかないので、状態 q1 に移ります。組 ⟨{q1}, 1001⟩ を覚えておきます。オートマトンは次のようになります。

先に進みましょう。語のまだ読んでいない部分は1001なので、再び1の遷移を探します。今度は2本あり、状態 q0 にもどる遷移と、状態 q3 に進む遷移です。ここでは両方の状態に同時に進みます。そこで、組 ⟨{q0, q3}, 001⟩ を覚えておきます。

なぜこうするのでしょうか。初期状態から語11によって、状態 q3 にも状態 q0 にも行ける道があることはわかっています。どちらの枝が役に立つかはまだわからないので、とにかく両方を覚えておくのです。先に進みましょう。入力は記号0です。今度は、q0 からの遷移と q3 からの遷移の両方を見なければなりません。状態 q0 からは状態 q1 と q2 への遷移が、状態 q3 からは q1 にもどる遷移が出ています。これらの状態をすべて集めると、組 ⟨{q1, q2}, 01⟩ が得られます。

しかし、このステップはまだ終わっていません。状態 q2 から状態 q4 への ε 遷移があります。ε 遷移は、次の文字を読まずに「いつでも進める」のでした。言いかえると、入力語から次の記号を読まずに、さらに状態 q4 に移ることができます。そこで、今いる可能性のある状態に状態 q4 を加えて、⟨{q1, q2, q4}, 01⟩ とします。オートマトンに色をつけると、次のようになります。

次の入力は0です。今いる3つの状態から、どこへ移れるかを調べます。q1 からは0の遷移がないので、どこへも移れません。この枝の計算はここで終わります。状態 q4 からも、どこへも進めません。状態 q2 からは、状態 q2 にもどります。しかし、状態 q2 からは再び状態 q4 への ε 遷移が出ているので、状態 q4 にも進めます。したがって、状態の集合 {q1, q2, q4} からは、記号0によって状態の集合 {q2, q4} に進めます。こうして組 ⟨{q2, q4}, 1⟩ が得られます。

最後に、入力は記号1です。状態 q2 からはどこへも進めないので、この枝の計算はここで終わります。一方、状態 q4 からは状態 q3 と q5 に進めます。組 ⟨{q3, q5}, ε⟩ が得られます。

これでオートマトンは語全体を読み終え、2つの状態 q3, q5 に「いる」ことになります。そのうち少なくとも1つは受理状態なので、オートマトンは語11001を受理します。

アルゴリズム

まず、状態の集合を引数にとり、そこから ε 遷移によってさらにどの状態に進めるかを計算する関数が必要です。入力はオートマトン ⟨Q, Σ, δ, q0, F⟩ と状態の集合 States で、出力はこれらの状態に、ε 遷移で進める状態を加えた集合です。この操作を、集合 States の ε 閉包といいます。

FunctionEpsilonClosure(⟨Q,Σ,δ,q0,F⟩,States)NextStates←∅ForEachq∈StatesDoNextStates←NextStates∪{q}∪δ(q,ε)EndForIfNextStates∖States=∅DoReturnStatesElseReturnEpsilonClosure(⟨Q,Σ,δ,q0,F⟩,NextStates)EndIfEndFunction

この関数を、非決定性オートマトンの動作をシミュレートする関数の中で使います。入力はオートマトン、入力語 w = w1w2… wn、そしてオートマトンが今いる状態の集合です。出力は、オートマトンがその語を受理するかしないかに応じて、YES か NO の答えです。

FunctionComputeNFA(⟨Q,Σ,δ,q0,F⟩,w,States)Ifw=ϵDoIfStates∩F≠∅DoReturnYESElseReturnNOEndIfEndIfNextStates←∅ForEachq∈StatesDoNextStates←NextStates∪δ(q,w1)EndForNextStates←EpsilonClosure(⟨Q,Σ,δ,q0,F⟩,NextStates)ReturnComputeNFA(⟨Q,Σ,δ,q0,F⟩,w2w3…wn,NextStates)EndFunction

オートマトンが語 w を受理するかどうかを調べたいときは、関数を ComputeNFA(⟨Q, Σ, δ, q0, F⟩, w, EpsilonClosure(⟨Q, Σ, δ, q0, F⟩, {q0})) のように呼び出します。

参考文献

章: 有限オートマトン, 完全なオートマトン, 非決定性有限オートマトン, 非決定性オートマトンのシミュレーション, NFA から DFA への変換