Aritalab:Lecture/Automata/Regular
m (→ε入力つき非決定性有限オートマトン) |
m (→有限オートマトンの能力) |
||
(31 intermediate revisions by one user not shown) | |||
Line 1: | Line 1: | ||
− | == | + | {| style="float:right" |
− | + | |__TOC__ | |
+ | |} | ||
+ | [[Aritalab:Lecture/Automata|形式言語理論のトップページヘ]] | ||
+ | ==まとめ== | ||
− | # Φ | + | ここでは正則表現(または正規表現)という表現形式であらわされる言語が、有限オートマトンが受理する言語と等価であることを示します。 |
− | # ε | + | 有限オートマトンの能力は、非決定性(同じ入力に対して移動先が複数あってよい)でもε入力つき(入力が無くても移動できる状態があってよい)でも変わりません。 |
− | # a ∈ Σ なら a | + | |
− | # R<sub>1</sub> と R<sub>2</sub> | + | ==正則表現== |
+ | アルファベット Σ 上の正則表現 (regular expression) S と、 S が表現する言語 L(S) を以下のように定義します。 | ||
+ | |||
+ | # Φ は正則表現であり、 L(Φ) = Φ | ||
+ | # ε は正則表現であり、 L(ε) = { ε } | ||
+ | # a ∈ Σ なら a は正則表現であり、 L(a) = { a } | ||
+ | # R<sub>1</sub> と R<sub>2</sub> が正則表現なら | ||
: R<sub>1</sub> | R<sub>2</sub> R<sub>1</sub>R<sub>2</sub> R<sub>1</sub><sup>*</sup> | : R<sub>1</sub> | R<sub>2</sub> R<sub>1</sub>R<sub>2</sub> R<sub>1</sub><sup>*</sup> | ||
− | + | も正則表現であり、それぞれ | |
: L( R<sub>1</sub> | R<sub>2</sub> ) = L(R<sub>1</sub>) ∪ L(R<sub>2</sub>) | : L( R<sub>1</sub> | R<sub>2</sub> ) = L(R<sub>1</sub>) ∪ L(R<sub>2</sub>) | ||
: L( R<sub>1</sub>R<sub>2</sub> ) = L(R<sub>1</sub>) L(R<sub>2</sub>) | : L( R<sub>1</sub>R<sub>2</sub> ) = L(R<sub>1</sub>) L(R<sub>2</sub>) | ||
: L( R<sub>1</sub><sup>*</sup> ) = L( R<sub>1</sub> )<sup>*</sup> | : L( R<sub>1</sub><sup>*</sup> ) = L( R<sub>1</sub> )<sup>*</sup> | ||
+ | 形式言語理論の人は正則表現、プログラミング言語の人は正規表現という言葉を使うことが多いようです。 | ||
==決定性有限オートマトン== | ==決定性有限オートマトン== | ||
状態 K, 入力記号 Σ, 受理状態 F ⊂ K の各有限集合を与えられたとき、決定性有限オートマトン (DFA: Deterministic Finite Automaton) は M = (K, Σ, δ, s<sub>0</sub>, F) で与えられます。s<sub>0</sub> ∈ K は初期状態、 δ : K × Σ → K は(状態)遷移関数と呼ばれます。 | 状態 K, 入力記号 Σ, 受理状態 F ⊂ K の各有限集合を与えられたとき、決定性有限オートマトン (DFA: Deterministic Finite Automaton) は M = (K, Σ, δ, s<sub>0</sub>, F) で与えられます。s<sub>0</sub> ∈ K は初期状態、 δ : K × Σ → K は(状態)遷移関数と呼ばれます。 | ||
Line 25: | Line 35: | ||
: 任意の s ∈ K, a ∈ Σ, x ∈ Σ<sup>*</sup> に対して δ'(s, ε) = s かつ δ'(s, xa) = δ(δ'(s, x), a) | : 任意の s ∈ K, a ∈ Σ, x ∈ Σ<sup>*</sup> に対して δ'(s, ε) = s かつ δ'(s, xa) = δ(δ'(s, x), a) | ||
この写像を、これからは δ と書きます。状態遷移関数を | この写像を、これからは δ と書きます。状態遷移関数を | ||
− | : δ : K × Σ<sup>*</sup> → K | + | : δ : K × Σ → K から K × Σ<sup>*</sup> → K |
に拡張したわけです。 | に拡張したわけです。 | ||
与えられた列 x を読み込み、最後に受理状態になったとき、その列は受理されたといいます。オートマトン M が受理する列の全体を、M が認識する言語といいます。 | 与えられた列 x を読み込み、最後に受理状態になったとき、その列は受理されたといいます。オートマトン M が受理する列の全体を、M が認識する言語といいます。 | ||
− | ; | + | ; Σ 上の言語 L を認識する DFA があるとき、L の補集合 Σ<sup>*</sup> - L を受理する DFA がある |
− | L を認識するDFAを M と書きます。M が受理しない列は、読み終わったときに非受理状態にあります。そのため、M | + | L を認識するDFAを M と書きます。M が受理しない列は、読み終わったときに非受理状態にあります。そのため、M における受理状態を非受理に、非受理状態を受理に全て入れ替えれば完成です。この補集合演算はプログラミング言語における正則表現の ^ 演算子として利用されています。 |
+ | |||
+ | 例として、C言語においては A, T, G, C だけを含む文字列を [ATGC]* と記述できます。そしてATGCを1つも含まない文字列は [^ATGC]* と記述できます。 | ||
+ | |||
+ | ; Σ 上の言語 L, L' を認識する DFA があるとき、L ∩ L' を受理する DFA がある | ||
+ | L, L' を認識するDFAをそれぞれ M = (K, Σ, δ, s<sub>0</sub>, F) , M' = (K', Σ, δ', s'<sub>0</sub>, F') と書きます。入力アルファベット Σ は共通にしておきます。ここで、DFA の状態の対 (s, s') ∈ K × K' を状態とし、入力 a ∈ Σ に対して、 (s, s') → ( δ(s, a), δ'(s', a) ) に遷移するオートマトン N を構築します。これは状態数がもとのオートマトンの直積に対応するので、'''直積オートマトン''' と呼ばれます。直積オートマトン N において、M と M' の初期状態の組を N の初期状態とし、M と M' の受理状態の組 (複数あればその全ての組み合わせ)を N の受理状態とします。こうすると N は L ∩ L' を受理します。 | ||
+ | |||
+ | ; Σ 上の言語 L, L' を認識する DFA があるとき、L ∪ L' を受理する DFA がある | ||
+ | L ∩ L' の場合と同じ議論で構築できます。この演算はプログラミング言語における正則表現の | 演算子に対応しています。 | ||
==非決定性有限オートマトン== | ==非決定性有限オートマトン== | ||
+ | Java で正規表現を使うとき、Pattern, Machter というクラスを利用します。[http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/regex/Pattern.html API仕様]にはいきなり「NFA ベースのマッチング」と書いてありますが、その NFA のことです。 | ||
非決定性オートマトン (NFA: Nondeterministic Finite Automaton) はDFAと同じ M = (K, Σ, δ, s<sub>0</sub>, F) で与えられます。異なるのは状態遷移関数だけで、 | 非決定性オートマトン (NFA: Nondeterministic Finite Automaton) はDFAと同じ M = (K, Σ, δ, s<sub>0</sub>, F) で与えられます。異なるのは状態遷移関数だけで、 | ||
: K × Σ → {s<sub>i</sub>,.. s<sub>j</sub> } (s ∈ K) | : K × Σ → {s<sub>i</sub>,.. s<sub>j</sub> } (s ∈ K) | ||
です。つまり、各入力記号に対して→が複数あってもよく、また存在しなくてもよいというものです。ここでも δ の定義を拡張します。 | です。つまり、各入力記号に対して→が複数あってもよく、また存在しなくてもよいというものです。ここでも δ の定義を拡張します。 | ||
− | : 任意の s ∈ K, a ∈ Σ, x ∈ Σ<sup>*</sup> に対して δ'(s, ε) = s かつ δ'(s, xa) = {q | p = δ'(s, x), q = δ(p,a) となる p ∈ K が存在する } | + | : 任意の s ∈ K, a ∈ Σ, x ∈ Σ<sup>*</sup> に対して δ'(s, ε) = s |
+ | : かつ δ'(s, xa) = { q | p = δ'(s, x), q = δ(p,a) となる p ∈ K が存在する } | ||
最後の「が存在する」という部分が重要です。与えられた記号列 x を読んで δ(s<sub>0</sub>, x) に少なくともひとつの受理状態が含まれれば、その列 x は受理されるといいます。いかなる受理状態にも到達できないときに、受理されないといいます。 | 最後の「が存在する」という部分が重要です。与えられた記号列 x を読んで δ(s<sub>0</sub>, x) に少なくともひとつの受理状態が含まれれば、その列 x は受理されるといいます。いかなる受理状態にも到達できないときに、受理されないといいます。 | ||
− | ; | + | ; Σ 上の言語 L を認識する NFA があるとき、L の補集合 Σ<sup>*</sup> - L を受理する NFA がある |
これはDFAのときと同じで、受理状態と非受理状態をすべて入れ替えれば構築できます。 | これはDFAのときと同じで、受理状態と非受理状態をすべて入れ替えれば構築できます。 | ||
Line 52: | Line 72: | ||
んで到達できる状態集合を以下のように定義します。 | んで到達できる状態集合を以下のように定義します。 | ||
: S<sub>2</sub> = { q | q ∈ δ(p,a) となる状態 p ∈ S<sub>1</sub>が存在する } | : S<sub>2</sub> = { q | q ∈ δ(p,a) となる状態 p ∈ S<sub>1</sub>が存在する } | ||
− | ここで、S<sub>1</sub> S<sub>2</sub> をそれぞれ単独の状態とみなせば、入力記号 a によって S<sub>1</sub> → S<sub>2</sub> と遷移することにおなじです。つまり、N の状態集合の部分集合のそれぞれを状態とするDFA ''D''を構成し、初期状態として S<sub>0</sub> = { s<sub>0</sub>} | + | ここで、S<sub>1</sub> S<sub>2</sub> をそれぞれ単独の状態とみなせば、入力記号 a によって S<sub>1</sub> → S<sub>2</sub> と遷移することにおなじです。つまり、N の状態集合の部分集合のそれぞれを状態とするDFA ''D'' を構成し、初期状態として S<sub>0</sub> = { s<sub>0</sub>} を読み替えれば構成できます。こうすると状態数は指数的に増えてしまいますが、受理できる言語のクラスは NFA も DFA も変わらないということになります。 NFA ''N'' の状態数を n とすると、DFA ''D'' の状態数は 2<sup>n</sup> が上限になります。 |
+ | 実際に 2<sup>n</sup> レベルの状態を必要とする NFA の族が [http://ci.nii.ac.jp/naid/110003191841 2000 年に論文]になりました<ref>松浦、岩間、Paterson「2^n-α個の決定性状態を要するn状態NFAの族について」電子情報通信学会技術研究報告. COMP, コンピュテーション 100(402), 17-24, 2000-10-20</ref>。 | ||
==ε入力つき非決定性有限オートマトン== | ==ε入力つき非決定性有限オートマトン== | ||
− | ε入力つき非決定性有限オートマトン (εNFA) では、入力記号を読まずに遷移することが許されます。この名前は、記号なしの遷移のときに形式的に入力記号εを読むと仮定するからです。状態遷移関数の定義は | + | ε入力つき非決定性有限オートマトン (εNFA) では、入力記号を読まずに遷移することが許されます。この名前は、記号なしの遷移のときに形式的に入力記号 ε を読むと仮定するからです。状態遷移関数の定義は |
: K × Σ ∪ ε → {s<sub>i</sub>,.. s<sub>j</sub> } (s ∈ K) | : K × Σ ∪ ε → {s<sub>i</sub>,.. s<sub>j</sub> } (s ∈ K) | ||
となります。 | となります。 | ||
Line 62: | Line 83: | ||
===εNFAとNFAの等価性=== | ===εNFAとNFAの等価性=== | ||
− | + | 遷移する手段が多い εNFA のほうが NFA よりも受理できる記号列の幅が広いように思われますが、これもそうではありません。 | |
− | εNFA ''E'' が与えられたら、''E'' の各状態から ε | + | εNFA ''E'' が与えられたら、''E'' の各状態から ε遷移によって移動できる状態の集合を一つの状態とみなす NFA ''N'' を構築できます。''N'' の状態数は ''E'' と同じで、εNFA が受理する言語を全て受理することができます。 |
ここまでの話を総合すると、 | ここまでの話を総合すると、 | ||
− | : | + | : DFAで受理できる言語のクラス = NFAで受理できる言語のクラス = εNFAで受理できる言語のクラス |
となります。 | となります。 | ||
− | == | + | ==正則表現とNFAの等価性== |
− | + | ||
− | + | どうして εNFA を導入したのでしょうか。それは正則表現をオートマトンで表現するのに、ε遷移があると便利だからです。 | |
+ | 正則表現は再帰的に構成されていますが、その構成法をそのまま εNFA でシミュレートできます。 | ||
# Φの場合、枝の無い状態に対応 | # Φの場合、枝の無い状態に対応 | ||
# 空文字 ε の場合、ε遷移に対応 | # 空文字 ε の場合、ε遷移に対応 | ||
# 記号 a ∈ Σ の場合、a による遷移に対応 | # 記号 a ∈ Σ の場合、a による遷移に対応 | ||
− | # R<sub>1</sub>R<sub>2</sub>の場合、R<sub>1</sub>に対応するオートマトンの受理状態と、R<sub>2</sub>に対応するオートマトンの初期状態をε遷移で直列に結ぶ。 | + | # R<sub>1</sub>R<sub>2</sub>の場合、R<sub>1</sub> に対応するオートマトンの受理状態と、R<sub>2</sub> に対応するオートマトンの初期状態をε遷移で直列に結ぶ。 |
# R<sub>1</sub><sup>*</sup>の場合、R<sub>1</sub>に対応するオートマトンの受理状態と初期状態をε遷移で結ぶ。 | # R<sub>1</sub><sup>*</sup>の場合、R<sub>1</sub>に対応するオートマトンの受理状態と初期状態をε遷移で結ぶ。 | ||
− | # R<sub>1</sub> | R<sub>2</sub>の場合、R<sub>1</sub> | + | # R<sub>1</sub> | R<sub>2</sub>の場合、R<sub>1</sub>とR<sub>2</sub>に対応するオートマトンの初期状態どうしと、受理状態どうしをそれぞれε遷移で並列に結ぶ。 |
− | + | こうすると、正則表現 R で表現される言語 L を受理する εNFA ができました。 | |
− | === | + | ===NFAと正則表現の等価性=== |
− | + | 正則表現で構成される言語が εNFA で認識できても、その逆が言えないと等価性は示せません。ある言語 L が NFA で受理されるなら、L を正則表現 R で表現できることを示しましょう。 | |
− | まず NFA N | + | まず NFA ''N'' が受理する正則表現を Reg( N ) とあらわし、Reg の中身を徐々に分解していくことを考えます。 |
状態 s から s' への記号 a による遷移に注目しましょう(遷移 A とします)。この遷移1つだけを N から取り除いた NFA を N' とします。 そして、オートマトン N' の構造を保ったまま初期状態を s<sub>0</sub>、終了状態の集合を F に置き換えたものを N'(s<sub>0</sub>, F) のように書くことにします。 | 状態 s から s' への記号 a による遷移に注目しましょう(遷移 A とします)。この遷移1つだけを N から取り除いた NFA を N' とします。 そして、オートマトン N' の構造を保ったまま初期状態を s<sub>0</sub>、終了状態の集合を F に置き換えたものを N'(s<sub>0</sub>, F) のように書くことにします。 | ||
Line 95: | Line 117: | ||
* N' (s<sub>0</sub>, {s}) → [s から s' へ aで遷移 (複数回あるかもしれない)] → N' (s', F) | * N' (s<sub>0</sub>, {s}) → [s から s' へ aで遷移 (複数回あるかもしれない)] → N' (s', F) | ||
という流れでオートマトン中を遷移していきます。 | という流れでオートマトン中を遷移していきます。 | ||
− | したがって、N | + | したがって、N が受理する正則表現は |
* Reg( N'(s<sub>0</sub>, F) ) | * Reg( N'(s<sub>0</sub>, F) ) | ||
* Reg( N'(s<sub>0</sub>, {s}) ) [a Reg( N'(s', {s}) )]<sup>*</sup> a Reg( N'(s', F) ) | * Reg( N'(s<sub>0</sub>, {s}) ) [a Reg( N'(s', {s}) )]<sup>*</sup> a Reg( N'(s', F) ) | ||
− | を | | + | を | (または) でつないだ正則表現に分解できます。スター演算の繰り返しが0回の可能性があるため、 [ ... ]<sup>*</sup> のあとに a で一回遷移して、Reg(N'(s', F)) につないでいる点に注意してください。 |
− | + | もとの正則表現は、遷移 A を取り除いてサイズが1つ小さくなったオートマトンを意味する正則表現を用いて構成できました。これを再帰的にずっと繰り返していくと、最後は遷移の枝が0本のオートマトンを意味する正則表現だけで構築できます。遷移の枝が0のオートマトン M を表現する正則表現は | |
* M の初期状態が受理状態なら ε | * M の初期状態が受理状態なら ε | ||
* M の初期状態が受理状態でないなら Φ | * M の初期状態が受理状態でないなら Φ | ||
− | で置き換えます。結果として、任意の NFA | + | で置き換えます。結果として、任意の NFA が正則表現で表せることがわかります。 |
+ | |||
+ | ここまでの話を総合すると、NFAと正則表現が等価であることがわかり、結局、正則表現は DFA と等価なことが示せました。 | ||
+ | |||
+ | ==有限オートマトンの能力== | ||
+ | 有限オートマトンはその名のとおり状態が有限個しかなく、その範囲でしか(状態の)記憶ができないため認識できる言語は大きく限られます。直感的には数を憶えておく作業ができないと考えればよいでしょう。以下の結果は、[[Aritalab:Lecture/Automata/PumpingLemma|ポンピング補題]]としてまとめられ、[[Aritalab:Lecture/Automata/ContextFree|文脈自由言語]]にも対応する補題が存在します。 | ||
+ | |||
+ | ; 言語 {0<sup>n</sup>1<sup>n</sup> | n ≥ 0 } は、いかなる有限オートマトンでも認識できない | ||
+ | 上の言語を受理するオートマトン M があると仮定し、その状態数を k とします。 n ≥ k とすると、M の初期状態から記号を k+1 個読んだとき、たどってきた状態の中には少なくとも2個同じ状態が現われます。 | ||
+ | それらの状態に達するまでに読み込む0の数をそれぞれ i, j (i ≠ j) とすると、M は 0<sup>i</sup>1<sup>i</sup> を受理するのと同時に、0<sup>j</sup>1<sup>i</sup> も受理してしまうので矛盾します。 | ||
+ | |||
+ | ; 括弧の対応は、いかなる有限オートマトンでも認識できない | ||
+ | 上から導かれる事実として、入れ子になった括弧の対応はオートマトンで検出できないことがわかります。 | ||
+ | : (((..(( 例 ))..))) | ||
+ | のように書かれても左右の括弧を数えられないからです。 | ||
+ | |||
+ | ; 言語 { xx | x ∈ { 0, 1 }<sup>*</sup> } は、いかなる有限オートマトンでも認識できない | ||
+ | 言語 x の具体例として 0<sup>n</sup> を考えてみます。0<sup>2n</sup>だけを認識するオートマトンは、言語 {0<sup>n</sup>1<sup>n</sup> | n ≥ 0 } の場合と同じ証明手順により、構築できないことがわかります。 | ||
+ | |||
+ | 上記の例はいずれも、n が無限である点に注意してください。例えば、言語 {0<sup>n</sup>1<sup>n</sup> | 10000 ≥ n ≥ 0 } であれば有限オートマトンで認識できます。しかし、括弧の対応を認識できない事実は致命的です。全てのプログラミング言語を含む、括弧の対応づけを要する言語を有限オートマトンは認識できないことを意味するからです。 | ||
+ | |||
+ | |||
+ | |||
+ | <div align=right> | ||
+ | 次→ [[Aritalab:Lecture/Automata/ContextFree|文脈自由言語]] | ||
+ | </div> | ||
− | + | <references/> |
Latest revision as of 10:41, 7 May 2013
|
[edit] まとめ
ここでは正則表現(または正規表現)という表現形式であらわされる言語が、有限オートマトンが受理する言語と等価であることを示します。 有限オートマトンの能力は、非決定性(同じ入力に対して移動先が複数あってよい)でもε入力つき(入力が無くても移動できる状態があってよい)でも変わりません。
[edit] 正則表現
アルファベット Σ 上の正則表現 (regular expression) S と、 S が表現する言語 L(S) を以下のように定義します。
- Φ は正則表現であり、 L(Φ) = Φ
- ε は正則表現であり、 L(ε) = { ε }
- a ∈ Σ なら a は正則表現であり、 L(a) = { a }
- R1 と R2 が正則表現なら
- R1 | R2 R1R2 R1*
も正則表現であり、それぞれ
- L( R1 | R2 ) = L(R1) ∪ L(R2)
- L( R1R2 ) = L(R1) L(R2)
- L( R1* ) = L( R1 )*
形式言語理論の人は正則表現、プログラミング言語の人は正規表現という言葉を使うことが多いようです。
[edit] 決定性有限オートマトン
状態 K, 入力記号 Σ, 受理状態 F ⊂ K の各有限集合を与えられたとき、決定性有限オートマトン (DFA: Deterministic Finite Automaton) は M = (K, Σ, δ, s0, F) で与えられます。s0 ∈ K は初期状態、 δ : K × Σ → K は(状態)遷移関数と呼ばれます。
DFAは与えられた列が言語に属するかどうかを判定する機械と考えるとわかりやすいです。普通は状態を○、状態間の遷移を→、受理状態を◎であらわします。例えば以下は、状態1において入力記号 0 を読み込み、状態2に移動することを意味しています。
0
① −−− ②
これを δ(s1, 0) = s2 と書きます。DFAにおける各状態は、全ての入力記号に対応する状態遷移関数(→)を持っています。
この定義だと、状態遷移関数が入力記号毎に与えられていますが、これは列へと自然に拡張できます。まずは再帰的な写像 δ' を用意します。
- 任意の s ∈ K, a ∈ Σ, x ∈ Σ* に対して δ'(s, ε) = s かつ δ'(s, xa) = δ(δ'(s, x), a)
この写像を、これからは δ と書きます。状態遷移関数を
- δ : K × Σ → K から K × Σ* → K
に拡張したわけです。
与えられた列 x を読み込み、最後に受理状態になったとき、その列は受理されたといいます。オートマトン M が受理する列の全体を、M が認識する言語といいます。
- Σ 上の言語 L を認識する DFA があるとき、L の補集合 Σ* - L を受理する DFA がある
L を認識するDFAを M と書きます。M が受理しない列は、読み終わったときに非受理状態にあります。そのため、M における受理状態を非受理に、非受理状態を受理に全て入れ替えれば完成です。この補集合演算はプログラミング言語における正則表現の ^ 演算子として利用されています。
例として、C言語においては A, T, G, C だけを含む文字列を [ATGC]* と記述できます。そしてATGCを1つも含まない文字列は [^ATGC]* と記述できます。
- Σ 上の言語 L, L' を認識する DFA があるとき、L ∩ L' を受理する DFA がある
L, L' を認識するDFAをそれぞれ M = (K, Σ, δ, s0, F) , M' = (K', Σ, δ', s'0, F') と書きます。入力アルファベット Σ は共通にしておきます。ここで、DFA の状態の対 (s, s') ∈ K × K' を状態とし、入力 a ∈ Σ に対して、 (s, s') → ( δ(s, a), δ'(s', a) ) に遷移するオートマトン N を構築します。これは状態数がもとのオートマトンの直積に対応するので、直積オートマトン と呼ばれます。直積オートマトン N において、M と M' の初期状態の組を N の初期状態とし、M と M' の受理状態の組 (複数あればその全ての組み合わせ)を N の受理状態とします。こうすると N は L ∩ L' を受理します。
- Σ 上の言語 L, L' を認識する DFA があるとき、L ∪ L' を受理する DFA がある
L ∩ L' の場合と同じ議論で構築できます。この演算はプログラミング言語における正則表現の | 演算子に対応しています。
[edit] 非決定性有限オートマトン
Java で正規表現を使うとき、Pattern, Machter というクラスを利用します。API仕様にはいきなり「NFA ベースのマッチング」と書いてありますが、その NFA のことです。 非決定性オートマトン (NFA: Nondeterministic Finite Automaton) はDFAと同じ M = (K, Σ, δ, s0, F) で与えられます。異なるのは状態遷移関数だけで、
- K × Σ → {si,.. sj } (s ∈ K)
です。つまり、各入力記号に対して→が複数あってもよく、また存在しなくてもよいというものです。ここでも δ の定義を拡張します。
- 任意の s ∈ K, a ∈ Σ, x ∈ Σ* に対して δ'(s, ε) = s
- かつ δ'(s, xa) = { q | p = δ'(s, x), q = δ(p,a) となる p ∈ K が存在する }
最後の「が存在する」という部分が重要です。与えられた記号列 x を読んで δ(s0, x) に少なくともひとつの受理状態が含まれれば、その列 x は受理されるといいます。いかなる受理状態にも到達できないときに、受理されないといいます。
- Σ 上の言語 L を認識する NFA があるとき、L の補集合 Σ* - L を受理する NFA がある
これはDFAのときと同じで、受理状態と非受理状態をすべて入れ替えれば構築できます。
[edit] NFAとDFAの等価性
NFAのほうがDFAよりも受理できる記号列の幅が広いように思われますが、実際はそうではありません。 部分集合構成という手法を用いて、言語 L がNFA N で認識されるとき、対応するDFA D でも認識できることを示します。 N の状態集合の任意の部分集合を S1 とします。初期状態から列をある程度読んだときに、S1 のどの状態にも到達可能と仮定しましょう。そのとき、更に記号 a を読 んで到達できる状態集合を以下のように定義します。
- S2 = { q | q ∈ δ(p,a) となる状態 p ∈ S1が存在する }
ここで、S1 S2 をそれぞれ単独の状態とみなせば、入力記号 a によって S1 → S2 と遷移することにおなじです。つまり、N の状態集合の部分集合のそれぞれを状態とするDFA D を構成し、初期状態として S0 = { s0} を読み替えれば構成できます。こうすると状態数は指数的に増えてしまいますが、受理できる言語のクラスは NFA も DFA も変わらないということになります。 NFA N の状態数を n とすると、DFA D の状態数は 2n が上限になります。 実際に 2n レベルの状態を必要とする NFA の族が 2000 年に論文になりました[1]。
[edit] ε入力つき非決定性有限オートマトン
ε入力つき非決定性有限オートマトン (εNFA) では、入力記号を読まずに遷移することが許されます。この名前は、記号なしの遷移のときに形式的に入力記号 ε を読むと仮定するからです。状態遷移関数の定義は
- K × Σ ∪ ε → {si,.. sj } (s ∈ K)
となります。 入力記号が無くても遷移できてしまうため、特定の状態に「落ち着く」ことがなくなります。入力記号の長さと遷移の数の対応も取れなくなります。 初期状態から記号列 x を読み込んで終了状態まで到達可能であれば、列 x は受理されると考えてください。(正確にはオートマトンの動作途中の状況を表わす概念を導入しないといけません。これを様相といいます。)
[edit] εNFAとNFAの等価性
遷移する手段が多い εNFA のほうが NFA よりも受理できる記号列の幅が広いように思われますが、これもそうではありません。 εNFA E が与えられたら、E の各状態から ε遷移によって移動できる状態の集合を一つの状態とみなす NFA N を構築できます。N の状態数は E と同じで、εNFA が受理する言語を全て受理することができます。
ここまでの話を総合すると、
- DFAで受理できる言語のクラス = NFAで受理できる言語のクラス = εNFAで受理できる言語のクラス
となります。
[edit] 正則表現とNFAの等価性
どうして εNFA を導入したのでしょうか。それは正則表現をオートマトンで表現するのに、ε遷移があると便利だからです。 正則表現は再帰的に構成されていますが、その構成法をそのまま εNFA でシミュレートできます。
- Φの場合、枝の無い状態に対応
- 空文字 ε の場合、ε遷移に対応
- 記号 a ∈ Σ の場合、a による遷移に対応
- R1R2の場合、R1 に対応するオートマトンの受理状態と、R2 に対応するオートマトンの初期状態をε遷移で直列に結ぶ。
- R1*の場合、R1に対応するオートマトンの受理状態と初期状態をε遷移で結ぶ。
- R1 | R2の場合、R1とR2に対応するオートマトンの初期状態どうしと、受理状態どうしをそれぞれε遷移で並列に結ぶ。
こうすると、正則表現 R で表現される言語 L を受理する εNFA ができました。
[edit] NFAと正則表現の等価性
正則表現で構成される言語が εNFA で認識できても、その逆が言えないと等価性は示せません。ある言語 L が NFA で受理されるなら、L を正則表現 R で表現できることを示しましょう。
まず NFA N が受理する正則表現を Reg( N ) とあらわし、Reg の中身を徐々に分解していくことを考えます。 状態 s から s' への記号 a による遷移に注目しましょう(遷移 A とします)。この遷移1つだけを N から取り除いた NFA を N' とします。 そして、オートマトン N' の構造を保ったまま初期状態を s0、終了状態の集合を F に置き換えたものを N'(s0, F) のように書くことにします。
N が受理する記号列は、
- 遷移 A を使う場合(複数回かもしれない)
- 遷移 A を使わない場合
とに分けられ、それぞれの場合は
- N' (s0, F)
- N' (s0, {s}) → [s から s' へ aで遷移 (複数回あるかもしれない)] → N' (s', F)
という流れでオートマトン中を遷移していきます。 したがって、N が受理する正則表現は
- Reg( N'(s0, F) )
- Reg( N'(s0, {s}) ) [a Reg( N'(s', {s}) )]* a Reg( N'(s', F) )
を | (または) でつないだ正則表現に分解できます。スター演算の繰り返しが0回の可能性があるため、 [ ... ]* のあとに a で一回遷移して、Reg(N'(s', F)) につないでいる点に注意してください。
もとの正則表現は、遷移 A を取り除いてサイズが1つ小さくなったオートマトンを意味する正則表現を用いて構成できました。これを再帰的にずっと繰り返していくと、最後は遷移の枝が0本のオートマトンを意味する正則表現だけで構築できます。遷移の枝が0のオートマトン M を表現する正則表現は
- M の初期状態が受理状態なら ε
- M の初期状態が受理状態でないなら Φ
で置き換えます。結果として、任意の NFA が正則表現で表せることがわかります。
ここまでの話を総合すると、NFAと正則表現が等価であることがわかり、結局、正則表現は DFA と等価なことが示せました。
[edit] 有限オートマトンの能力
有限オートマトンはその名のとおり状態が有限個しかなく、その範囲でしか(状態の)記憶ができないため認識できる言語は大きく限られます。直感的には数を憶えておく作業ができないと考えればよいでしょう。以下の結果は、ポンピング補題としてまとめられ、文脈自由言語にも対応する補題が存在します。
- 言語 {0n1n | n ≥ 0 } は、いかなる有限オートマトンでも認識できない
上の言語を受理するオートマトン M があると仮定し、その状態数を k とします。 n ≥ k とすると、M の初期状態から記号を k+1 個読んだとき、たどってきた状態の中には少なくとも2個同じ状態が現われます。 それらの状態に達するまでに読み込む0の数をそれぞれ i, j (i ≠ j) とすると、M は 0i1i を受理するのと同時に、0j1i も受理してしまうので矛盾します。
- 括弧の対応は、いかなる有限オートマトンでも認識できない
上から導かれる事実として、入れ子になった括弧の対応はオートマトンで検出できないことがわかります。
- (((..(( 例 ))..)))
のように書かれても左右の括弧を数えられないからです。
- 言語 { xx | x ∈ { 0, 1 }* } は、いかなる有限オートマトンでも認識できない
言語 x の具体例として 0n を考えてみます。02nだけを認識するオートマトンは、言語 {0n1n | n ≥ 0 } の場合と同じ証明手順により、構築できないことがわかります。
上記の例はいずれも、n が無限である点に注意してください。例えば、言語 {0n1n | 10000 ≥ n ≥ 0 } であれば有限オートマトンで認識できます。しかし、括弧の対応を認識できない事実は致命的です。全てのプログラミング言語を含む、括弧の対応づけを要する言語を有限オートマトンは認識できないことを意味するからです。
次→ 文脈自由言語
- ↑ 松浦、岩間、Paterson「2^n-α個の決定性状態を要するn状態NFAの族について」電子情報通信学会技術研究報告. COMP, コンピュテーション 100(402), 17-24, 2000-10-20