形式語言與自動機學習複述筆記

形式語言與自動機學習複述筆記

本文說明

本文內容僅僅是稻雲麥花根據自己所學的記憶,儘量不看書或其它的資料,僅僅憑藉鬧鐘記憶和理解,嘗試複述的筆記。寫的目的是爲了檢驗自己是否真正掌握所學內容。因此,有些東西可能我自覺已經是先修課的知識或者是此課程內容但是過於簡單基礎無需贅述。
至於爲何不直接說而是以寫下來的方式,是因爲沒有找到一個聽衆,感覺自言自語很神經病,並且在自習室說話估計會被打死。另外寫下來方便自己檢查。同時,有時候說實際上會比較模糊,自己不知道自己在說什麼。
至於爲什麼不用筆寫,我只想說可能是字太多了,手要寫斷。好吧,真相是我上次動筆寫字貌似是考試的時候,emmmm…
理直氣壯 表情
其實下次可以帶個筆紙,在湖邊邊說邊寫,這樣應該就不會被打擾自習室或者圖書館的人了,說只要是文字,筆寫關鍵部分的表達式或者圖就好。
溜了溜了 表情

文法

G=<V,T,P,S>
V是變元的集合。
T是終結符的集合,也就是字母表。
P是產生式,產生式都是左部推導出右部的形式。產生式都是左部推導出右部的形式,左部右部都是由變元和終結符並起來的集合中任意選元素組合而成。左部要求至少包含一個變元。
S是變元中的一個特殊元素,開始推導變元,指定從什麼變元開始。前三個集合不變,更換S會導致語言推出來的句子發生改變。

文法的喬姆斯基分類

文法的喬姆斯基分類是根據產生是的形式對文法進行分類。

  1. 左部右部不施加額外限制就是0型文法,又稱短語結構文法(PSG)。推導出的文法就是短語結構語言(PSL)。
  2. 左部長度不超過右部,就是1型文法、上下文有關文法。CSG CSL 上下文有關語言。
  3. 左部是單個的變元,右部不限制,就是2型文法、上下文無關文法, CFG CFL 上下文無關語言
  4. 左部是單個的變元,右部是單個的字母或者單個字母連接一個變元,就是3型文法、正則文法。RG RL 正則語言。

有窮自動機 正則語言 正則文法

關係

正則語言的集合和有窮自動機所能表示的集合是同樣的。即正則語言和有窮自動機可以互換,是等價的。而有窮自動機中有確定性有窮自動機DFA、非確定型有窮自動機NFA、帶空轉移的非確定型有窮自動機ϵ\epsilon-NFA。但是他們只是表現和描述形式不一樣,任意兩個之間都是“等價”的。它們所能表示的語言集合是同一個集合。引入後面兩種自動機是爲了方便人構造和描述自動機,而DFA是爲了方便證明即計算機自動實現。
正則語言和正則表達式可以互換。

正則表達式

正則表達式,實際上正則表達式就是集合的操作而已。只是沒有寫成集合的形式而已。
空集是正則表達式。空串ϵ\epsilon或者單獨一個字母表中的字母a都是表示包含一個元素的集合,是正則表達式。
三種操作,優先級從高到低是閉包運算(*)、連接運算(省略不寫)、並運算(+)。優先級相同時自左向右算。
連接運算是兩個集合A與B的相乘,只是得到的結果集合C的元素不是用<a,b>的序偶表示,而是直接ab直接連接起來,即字符串拼接。
容易知道並(+)可交換可結合,連接不可交換可結合。

正則表達式RE->ϵ\epsilon-NFA

基礎情況,空集和單點集容易構造有窮自動機。
對於並操作,A+B,只需要分別構造A和B的有窮自動機,然後增加一個初始狀態和接受狀態。初始狀態給A和B的初始狀態各自一個空轉移,A和B的接受狀態給都給新增的接受狀態一個空轉移即可。 看起來就是“並聯”電路而已。
對於連接操作符, 只需要A的接受狀態連一條空轉移邊到B的初始狀態即可。然後初始狀態設爲A的初始狀態,接受狀態設爲B的接受狀態即可。 看起來就是“串聯”電路而已。
閉包運算 A*,只需要新增一個初始狀態和一個接受狀態。初始到A初始,A接受到接受都有一條空轉移邊。對於原本的A的接受狀態連一條空轉移邊到A的初始狀態,讓自動機形成一個環可以不斷打轉實現任意正指數冪的功能。由於還有一個指數爲0的空串的情況,所以還需要初始到接受增加一條空轉移邊。

ϵ\epsilon-NFA->NFA(消除空轉移邊)

先說空閉包概念。


空閉包運算 ϵ\epsilon-閉包運算 ϵ-ENCLOSE(q)\epsilon \text{-ENCLOSE}(q)ϵ-ENCLOSE(A)\epsilon \text{-ENCLOSE}(A)
ϵ-ENCLOSE(q)\epsilon \text{-ENCLOSE}(q)

ϵ-ENCLOSE(q)\epsilon \text{-ENCLOSE}(q)函數的自變量是一個狀態,結果是一個狀態的集合。如何計算求解呢?從圖上直觀來講,就是從q狀態出發,只能沿着空轉移邊走,所能走到的所有點(狀態)就是這個集合中的元素,並且只有這些。當然,出發的點q肯定是可以走到的。

ϵ-ENCLOSE(A)\epsilon \text{-ENCLOSE}(A)

和上面這個不同的是,它的自變量是一個狀態的集合。運算結果依舊是一個狀態的集合。
運算方式就是A集合中的每一個狀態分別求ϵ\epsilon-閉包,然後將這些結果並起來就是狀態集合A求空閉包運算的結果。
當然,你也可以從這些A集合中的點出發沿着空轉移邊去染色,染上顏色的點就是A這個集合的空閉包運算結果。


消除空轉移邊得到NFA。
狀態集合不需要變更,字母表不需要變更。初始狀態也不變。
變的只是轉移函數(也就是畫圖中的邊)和可能需要做調整的接受狀態。
先看初始狀態q0q_0的空閉包有沒有接受狀態,也就是說q0q_0沿着空轉移邊能不能到達接受狀態。如果有,那說明去掉空轉移邊之後,q0q_0也要接受,加到接受狀態的集合裏即可。
然後就是轉移函數的變更了,非常簡單的理解就是,原本ϵ\epsilon-NFA中qiq_i沿着一條字母值爲a的邊到達的狀態的集合是A,而A的空閉包是B。那麼在NFA中我就需要qiq_i到B中的每一個狀態都連一條a邊。原因很簡單,原本的有空轉移的自動機裏面qiq_i沿着a邊到達了某個點,這個點接着一直沿着空轉移邊可以走到B中的每一個狀態那裏,現在把空轉移邊去掉了,自然需要補a邊了。
如此,ϵ\epsilon-NFA消除了空轉移邊變成了NFA.

NFA->DFA

NFA是讀入一個字符的時候,一個點出發可能有多條相同字母值的邊去往不同點。每一次的結果都是一個狀態的集合。初始的結果是{q0}\{q_0\}.讀入一個字符a,當前的結果的每一個點都沿着a邊往後走,只要有就加入到新結果的狀態集合中去。所有字符讀完了,只要結果的狀態集合中包含接受狀態就是接受這個字符串。
NFA轉換成DFA的樸素想法就是,NFA的狀態集合Q有n個狀態,那麼“結果的狀態集合”不就是Q的子集嗎?只需要把Q的每一個子集(共2n2^n個)都當作DFA的一個狀態,然後根據每一個子集讀不同的字母a在NFA中走出來的新的結果的狀態在DFA中連邊即可。
這個是從理論上說明了NFA一定可以變成DFA。
於是就出現了填表法。


上面的方法可行,但是會做很多不必要的工作,如有些子集可能在NFA的“行走”中根本不可能出現……
爲了避免搞很多多餘不必要的狀態,不必要的邊。
肯定不多餘的子集是{q0}\{q_0\}
對於一個不多餘的子集,我們要順着NFA去走不同字母的邊(如果有的話),去得到子集。這樣通過非多餘的子集走出來子集肯定不是多餘的。
如果最後所有的不多餘的子集都走完了。沒有新的子集了,那麼DFA就構造完畢了。

雖然最壞情況下,可能還是要出現2n2^n級別個狀態,但是,實際應用的時候,很多情況下DFA的狀態數並不是增長太多。

DFA->正則表達式

一種是類似於FLOYD的思想(我一下也不知道哪個先出的,我只知道我接觸的是FLOYD)。限制中間點不能超過k,然後逐步將k變大,當k到達n只是就是沒有限制了。
DFA的狀態從1編號到n.
R(k)(i,j)R^{(k)}(i,j)是一個正則表達式。這個正則表達式表示的語言是:從自動機的狀態i出發,到達狀態j,並且中間(不包含i,j)經過的狀態的編號都不超過k。

初始化(邊界情況是k=0的時候R(0)(i,j)R^{(0)}(i,j)):

iji \neq j

  1. i到j沒有邊,表達式是\empty
  2. i到j只有一條邊,字母值爲a的邊,則表達式是aa
  3. i到j只有多條邊,則表達式是這些邊的並 a1+a2+a3+...+ama_1+a_2+a_3+...+a_m
    i=ji=j,則i到j的字符串還可以是空串,因此,上面三種情況都需要並上空串ϵ\epsilon
迭代方式(轉移方程)

R(k)(i,j)=R(k1)(i,j)+R(k1)(i,k)(R(k1)(k,k))R(k1)(k,j)R^{(k)}(i,j)=R^{(k-1)}(i,j) + R^{(k-1)}(i,k)(R^{(k-1)}(k,k))^*R^{(k-1)}(k,j)
解釋:

  1. 中間點根本就不經過k,那麼就是k-1限制下所表示的R(k1)(i,j)R^{(k-1)}(i,j)
  2. 中間點經過了k,那麼就是三段,前面一段是i到k(沒有經過k,故事k-1限制下的),後面一段是k到j。中間是k到k的重複若干次(0,1,2,3,…次),故就是閉包。三段連接起來即可。

DFA->正則表達式的狀態消除法

大體就是邊上不在寫字母,而是寫正則表達式。
畫圖比較容易說明。

消除單個狀態

消除一個狀態並消除影響(補償)的方法。
因此只需要考慮前驅,自身(自環),後繼的問題。 拆分成三段,前驅到自身,自身自環轉轉轉(閉包),自身到後繼 這個用正則表達式表示出來併到前驅到後繼裏面即可。

整體消除方法

目標,僅僅保留初始狀態和接受狀態。(如果初始狀態剛好就是接受狀態,就只剩下一個狀態了)。
對於消除到最後的自動機就好表示成正則表達式了。

先不斷消除既非初始狀態也非接受狀態的狀態。 因爲這樣無需分類,消除了它,沒有影響。
接着要消除接受狀態了,這個必須分支。 例如消除s,雖然每個前驅s到後繼q的路徑補償了,但是消除掉s之後,初始狀態經s到其它接受狀態的是可以表示,但是隻走到s的理論上也要接受,但是s消除了無法接受。因此,要分支。
怎麼分支或者說消除的順序?
例如剩餘m個接受狀態p,q,r,s,t,…

  • 將p視作唯一接受狀態,不斷消除“非接受狀態”,因此我們得到了僅僅到p的接受狀態的自動機;
  • 消除p的時候,我們丟失了僅僅到p應該接受的表達式。但是到其它的狀態沒有丟失,至此,我們的需要求解的“接受狀態的數目”縮小了1。
    最後我們會得到m個簡單的自動機。分別是僅僅接受到狀態p,q,r,s,t…的自動機
    它們的表達式取並即可。

DFA最小化

DFA最小化就是將任意的DFA轉換爲等價的最小化的DFA(狀態數最少).
最小化的DFA中任意兩個狀態都是可區別的。

狀態可區別 可區別狀態對 等價狀態

兩個狀態q和p。
如果存在某一個字符串,分別從p和q出發,p和q走出來的結果(接受或拒絕)不一樣,那麼p和q就是可去別的狀態對。
與之相對的概念是狀態等價。即無論什麼字符串,從p和q出發,走出來的結果都是一樣的,要麼都是接受,要麼都是拒絕。對於等價狀態,顯然直觀上我們想讓它們合併讓狀態數變少。

如何確定哪些狀態兩兩可區分

當然,我們可以直接自己構造一個字符串,讓兩個狀態走出來的結果不一樣說明它們不可區分;但是,如果以下沒有構造出來可以走出不同的字符串,那麼就無法確定了;更糟糕的是是如果兩個狀態是等價的,那麼通過構造字符串,我們肯定是得到結果是一樣的,但是我們有不能直接肯定任意字符串,得到結果都是一樣的,那麼它們到底是不是可區分的就解決不了了。
因此,我們需要一定的方法。


  1. 接受狀態和非接受狀態肯定是不等價的,即可區分的。
  2. 如果對於同一個字母邊,p和q走出的狀態不等價,那麼p和q不等價。要斷言p和q不等價,要p和q沿着某一個字母值的邊到達的狀態中有兩個不等價就可以了;如果要斷言等價,就要各個字母的邊邊走到的狀態都是等價的纔行。

有了這兩條之後,我們就可以運用填表法來確定了。√ × ?(待定)
一輪一輪的掃描,如果某一輪之後沒有新增的可區分對,那麼說明所有的可區分對都找出來了。
然後把等價的狀態合併成一個,等價狀態內部的邊變成自環邊,入邊出邊進行整合合併,over。

正則語言的性質

泵引理

其實泵引理就是根據鴿籠原理,對於一個狀態數爲n的DFA,讀完長度大於等於n的字符串w之後,必然至少有一個狀態被訪問了兩次,換句話說,出現了一個環。然後對於那個環,可以不走,走1次,走2次,走3次……
因此,可以把這個環所對應的字符子串v去掉或重複若干次,得到的新字符串依舊和原本的結果是一樣的(接受或拒絕)。
Lnw(wLwn)即存在只依賴於正則語言L的常數n,\forall w(w \in L \wedge |w| \geq n),x,y,z(w=xyzyϵxyn(k(k0),xykzL))\exist x,y,z(w=xyz \wedge y \neq \epsilon \wedge |xy| \leq n \wedge (\forall k(k\geq 0),有xy^kz \in L)).

正則語言的泵引理敘述的是正則語言的性質。它的作用是,對於一個語言L,常常通過正則語言的泵引理來推導出矛盾(某個不應該接受的字符串被接受了),從而說明這個語言L不是正則語言。但是,通過正則語言的泵引理是無法證明一個語言是正則語言的,畢竟泵引理只是正則語言的必要條件(性質),而非充分條件。

正則語言的判定
  1. 正則語言的判定,即斷言一個語言是正則語言,通常是構造一個ϵ\epsilon-NFA(或NFA、DFA)接受這個語言或者構造正則表達式(基於的理論是已經證明了這些都是等價的……)。
  2. 根據已知的正則語言(可以通過第1條迅速證明)的正則語言及正則語言的封閉性(並、交、差、補、反轉、閉包、連接、同態、逆同態)來做運算得到要證明的語言L是正則語言。

上下文無關文法與下推自動機

TODO

圖靈機導引

TODO

二〇一九年六月六日
稻雲麥花

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章