從計算機CPU設計談P\NP問題(2),圖靈機

開天闢地發表於2014-11-04

1. 解剖圖靈機

    有關圖靈機及與圖靈機有關的p與np問題,已經成了數學與計算機界的大事。引用一段維基百科上的p與np的解釋。 
    複雜度類P即為所有可以由一個確定型圖靈機在多項式表達的時間內解決的問題;類NP由所有可以在多項式時間內驗證解是否正確的決定問題組成,或者等效的說,那些解可以在非確定型圖靈機上在多項式時間內找出的問題的集合。很可能,計算理論最大的未解決問題就是關於這兩類的關係的:P和NP相等嗎? 
    看來這確實是一件大事!做為計算機CPU設計方面的人士,不應該回避這種具有挑戰性的研究吧?

1.1. 圖靈機的狀態之謎

     圖靈機是神祕的機器,能夠很通俗地將它解釋清楚文章很難找到。為了透徹地理解圖靈,我們不妨先對圖靈機做一點詳細的研究。 

1.1.1. 圖靈機的數學定義

    關於圖靈機的數學定義一般介紹如下:
一臺圖靈機M是一個七元組,{Q,Σ,Γ,δ,q0,qaccept,qreject},其中 Q,Σ,Γ 都是有限集合,且滿足:
(1)Q 是有限狀態集合;
(2)Σ是輸入字母表,其中不包含特殊的空白符 □;
(3)Γ 是帶子上字母表,其中 □∈Γ且Σ Γ ;
(4)δ:Q×Γ→Q×Γ×{L,R}是轉移函式,其中L,R 表示讀寫頭是向左移還是向右移;
(5)q0∈Q是起始狀態;
(6)qaccept是接受狀態;
(7)qreject是拒絕接收狀態,且qreject≠qaccept。

    這是啥意思?定義的(2)和(3)條比較好理解,Σ可以理解成英文的字母符號表,最簡單的可以將它理解成Σ={0,1}。Γ是寫在帶子上的字母表,為了區分不同的連續表達的意思,用空格來區分這是書寫的常規。這裡說Σ Γ 是因為集合Γ比集合Σ多了一個空格字元□。我們用最簡單的情況考慮,理解Γ={0,1,□}。
    用一定位數的二進位制數可以表達語言文字。例如,8位的西文ascll編碼表,或者16位的unicode編碼表。16位的漢字編碼表等。將Γ帶子上的“格子”是理解成單獨的0和1,還是理解成由一定位數的二進位制編碼組織的資料?這是理解好圖靈機的一個關鍵問題。筆者認為,將帶子上的格子理解成能寫入固定位數的二進位制數較妥。
    定義中的(5)是任何機器執行所必需的條件。q0∈Q是說q0也是一個狀態,但究竟是怎樣的一種狀態?卻是留下了進一步想象的空間。
    定義(6)和(7)的理解是多樣的。接受狀態和拒絕狀態是指什麼?是指轉移函式接受讀寫頭讀出的字元還是拒絕?
    定義中最難理解的是δ:Q×Γ→Q×Γ×{L,R}這個轉移函式。關於轉移函式的討論,我們放到具體規範圖靈機之後。 

1.1.2. 圖靈機讀寫頭的狀態

    結合圖 2 1的圖靈機實體,我們有理由認為帶子上的每個格子是放置字元編碼的。這一點圖靈當時並沒有說清楚,只是說帶子上的格子寫上字元。不去探討原話如何了,我們就按著ascll編碼來討論,我想也不會丟掉圖靈機的精髓。

        [圖 2 1  圖靈機實體圖](d:/2014部落格/tulingji.gif)
    按著一般對圖靈機讀寫頭動作的描述,可以對讀寫頭的狀態描述如下。
(1)初始狀態,讀寫頭進入開始讀的位置;
(2)讀寫頭工作狀態;
(3)讀寫頭讀狀態;
(4)讀寫頭寫狀態;
(5)讀寫頭左移狀態;
(6)讀寫頭右移狀態;
(7)讀格接受傳輸狀態。
    讀寫頭進入這些狀態的條件是帶子上已經按規定寫好了由字元組成的問題。寫在帶子上的問題是在讀寫頭不工作的狀態下,用另外的方式(譬如人工方式)往格中寫上去的。  

1.1.3. 狀態集合Q

    圖靈機的狀態集Q都包含著哪些器件的狀態?這是具體化必須要給出回答的問題。為了說清楚這個問題,我們就以算術運算為例來研討。兩數x,y的加、減、乘、除的形式可以在帶子上這樣寫:+xy;-xy;×xy;÷xy。每種運算之間可以用一兩個空格隔開。
    假如已經經過了初始化,那麼開始讀到的“+”放在哪裡?是否能夠接受?如何判斷?接下來的數x和y以及讀入的空格,也面臨同樣的問題。這一系列問題圖靈先生似乎沒有告訴我們(我沒有讀過圖靈先生的原文,也許他說過了,但我沒見到。果真如此,敬請原諒。)但我們可以憑藉他的思想去構造,畢竟我們有現代計算機的設計方法。 

(1)判斷部件

    讀寫頭讀入資料時可以用對比的方法,檢查讀入資料的位數。例如二進位制數位數不足,可以自動依據資料的左側最高位數碼補齊位數(這涉及到限位數表示正負數,請參閱《自己設計製作CPU與微控制器》第10章p237)。如果格子中的數碼位數超過規定的位數,那麼就拒絕接受資料,並將讀寫頭拒絕接受狀態線置1,不然置0。初始狀態要將這個標誌線置0。
    讀寫頭接受讀入資料之後,要將資料傳輸給運算識別部件和運算部件。就算術運算來說,我們必須設計有對運算的種類判斷的功能,以便識別是不是這個圖靈機能夠完成的任務。這可以用一個暫存器ir接收運算子號加以識別。方法是用譯碼器將每種運算子“+”、“-”、“×”、“÷”的編碼轉化成一條狀態標誌線。初始化時,這些標誌線也要置0。不在能識別當中的編碼,可以設定一條“異常”的狀態標誌線來表示拒絕。 

(2)計算部件

    當判斷部件接受之後,讀寫頭繼續讀入的資料,要分別放入兩個暫存器才可以進行相應的運算。還要將運算的結果儲存在某個暫存器中。不同的運算要有分別控制的訊號線。各個暫存器都要有讀寫控制線。如果是所說的“查表”,那就要將每種運算列出參加運算的全部可能數,得出全部可能的結果。這些表要由運算種類線確定相應的運算表,找到結果再放入暫存器或向讀寫頭傳輸。這種查表的過程的複雜性可想而知。好在我們現在已經有了各種運算器,使圖靈所說的查表問題變得異常簡單了。 

(3)狀態集合Q的元素

    我們前面談到過裝置狀態線和能夠改變其狀態的控制線之間的關係,因而圖靈機就應該是一個有限狀態的機器。先來看δ:Q×Γ→Q×Γ×{L,R}這個轉移函式,你首先會問Q的元素是什麼?Γ集合我們已經理解了,它是帶子上能夠寫的資料集合。這裡Q做為一種狀態的集合提了出來,一定是表達圖靈機的狀態。我們應該理解到,這就是第1章中我們提到的CPU設計表真正的內容。由於器件的一種狀態都可以用一條導線表達出來。這種狀態標誌線的值直接受控制線的值和輸入資料的影響。因而可以認為狀態標誌線x是控制線的值q和輸入資料γ的函式,即有x=f(q,γ)。其中{q,γ} Q×Γ,q=q(t0),γ=γ(t0),t0是一個時間點。
    在下一個沒有資料和狀態變動的時間點t1(t1>t0)來考慮,狀態標誌線x又成了自變數,控制線又變成了狀態線的函式,即有q=g(x,t1)。顯然,我們將控制線全體q理解成狀態集合Q的元素是最合適不過了。
    不論什麼裝置的狀態線還是控制線,它們都是一條只取值0或1的邏輯變數。假設圖靈機有n條控制線,由於每一條控制線都只能取值0或1,那麼所能表達的全部狀態就只有2n個n位二進位制數。這個n位的二進位制數集就是Q。初始狀態q0自然是Q的元素,只是初始狀態的產生是由初始化過程決定的,而不是有前一時間點產生的控制狀態決定的。 

1.1.4. 如何理解讀寫頭左右移動

    將轉移函式δ:Q×Γ→Q×Γ×{L,R}中函式Q×Γ×{L,R}看成乘積空間同一時間點的值也許是一個誤解,這是因為讀寫頭在進行了寫之後,才能夠向左或者向右移動。所以轉移讀寫頭至少要分成2步完成,先要寫,然後才會移動。為了細緻地理解讀寫頭的工作過程,我們先來看維基百科是如何解釋圖靈機工作的。
    進入維基百科中文網站,在搜尋欄輸入圖靈機,立即會見到下面的內容。第一個叫圖靈機的正式定義。
    圖靈機M=(Q,Σ,Γ,δ,q0,qaccept,qreject)將以如下方式運作:
開始的時候將輸入符號串ω=ω0ω1…ωn-1  Σ*從左到右依此填在紙帶的第0,1,,…,n-1號格子上, 其他格子保持空白(即填以空白符□)。 M的讀寫頭指向第0號格子, M處於狀態q0。 機器開始執行後,按照轉移函式δ所描述的規則進行計算。 例如,若當前機器的狀態為q,讀寫頭所指的格子中的符號為x,設δ(q,x)=(q',x',L),則機器進入新狀態q', 將讀寫頭所指的格子中的符號改為x', 然後將讀寫頭向左移動一個格子。  若在某一時刻,讀寫頭所指的是第0號格子,但根據轉移函式它下一步將繼續向左移,這時它停在原地不動。   換句話說,讀寫頭始終不移出紙帶的左邊界。若在某個時刻M根據轉移函式進入了狀態qaccept,則它立刻停機並接受輸入的字串; 若在某個時刻M根據轉移函式進入了狀態qreject,則它立刻停機並拒絕輸入的字串。
    注意,轉移函式δ是一個部分函式,換句話說對於某些q,x,δ(q,x)可能沒有定義, 如果在執行中遇到下一個操作沒有定義的情況,機器將立刻停機。
    以上的這個解釋數實在是有些麩皮搔癢,難以讓人理解圖靈機讀寫頭移動的真諦。
    我們再來看第二個解釋,稱為圖靈機的基本術語。
設M=(Q,Σ,Γ,δ,q0,qaccept,qreject)是一臺圖靈機,
    1.M的帶描述(tape description)是一個函式F:N →Γ,其中F(i)表示M的帶上第i個格子中的符號;
    2.M的格局(configuration)是一個三元組(F,q,e),其中F:N →Γ是當前的帶描述,q Q是當前的狀態,e N是當前讀寫頭所處的位置;
    3.設C1=(F1,q1,e1), C2=(F2,q2,e2)是M的格局,設δ(q1,F(e1))=(q,x,d),若滿足q2=q,當d=L時e2=e1-1,當d=R時 e2=e1+1;以及當i≠e1時 F2(i)=F1(i) ,當i=e1時 F2(i)=x,則稱M從格局C1 產生格局C2,記作C1→C2。
    4.設C=(F,q,e)為M的格局,若q=qaccept則稱C為接受格局;若q=qreject則稱C為拒絕格局;接受格局和拒絕格局統稱為停機格局。
    設M是一臺圖靈機,將字串ω=ω0ω1…ωn-1作為其輸入,若存在格局序列C1,C2,…,Ck,使得
    1.C1是M在輸入ω上的起始格局,即C1=(F,q0,0),其中 當0≤i≤n-1時 F1(i)=ωi,其它情況 F1(i)=□。
    2.Ci→MCi+1,其中i=0,1,,…,k-1;
    3.Ck是接受格局。
則稱M 接受字串ω,且C1,C2,…,Ck稱為圖靈機M在輸入ω上的接受計算曆史。同理,若Ck是拒絕格局,則稱M 拒絕ω,且C1,C2,…,Ck稱為圖靈機M在輸入ω上的拒絕計算曆史。M所接受的所有字串的集合稱為M的語言,記作L(M)。
以上的解釋要比前面的解釋詳細得多了。這裡將讀寫頭的移動總結成自然數變數e的加減1運算,將每一次圖靈機狀態變化理解成“格局”,用格局的變化來解釋圖靈機的運算,並提出了接受格局和拒絕格局,但將這兩個格局統稱為停機恐怕有失水準。此外,引進自然數來描述讀寫頭的左右移動,未免將問題繁雜化了。而且加減1的動作如何讓讀寫頭完成,這又是一件很麻煩的事。 

1.1.5. 設計圖靈機

    利用圖靈機解題的過程可以叫做圖靈計算。讓我們依照圖靈的方法,進行一次最簡單的或、與、非的邏輯運算,來看看圖靈機到底是如何工作的吧。 

(1)用圖靈機解題的例子

    為了說明圖靈機的工作過程,我們來設計一個能夠解決邏輯運算的圖靈機。
    設M=(Q,Σ,Γ,δ,q0,qaccept,qreject)其中Σ={0,1,+,*,!},Γ={□,0,1,+,*,!}。符號“+”、“*”“!”分別表示或、與、非邏輯運算。帶子Γ的書寫格式為“!x” “+xy”“*xy”,即先寫運算子,後寫參加運算的數。還要求運算式之間以“□□□□”間隔。表 2 1是我們建立的圖靈機狀態變化表。表中的L、R表示讀寫頭左移一格或右移一格。每個字元佔用一格。運算結束後,至少要讀入一個空格,再進入下一計算的開始狀態。
    我們設計的圖靈機狀態轉換表如表 2 1所示。從表 2 1我們可以看到,這個圖靈機的狀態集合Q={x,y,z,z0,z1,f,f0,f1,end,erro},其中x是初始狀態,end是運算結束狀態,erro是停機拒絕狀態。表中symbol是讀寫頭每次讀入的內容,它與m-config一起構成了定義域元素,而經過操作行為Behaviour,其中包括移動讀寫頭和輸出(列印)資料,而轉化為最終狀態Final m-config。這一前一後的狀態轉換,形成了有序的運算操作,最終在不出現錯誤的情況下,得到運算的結果。注意,operations一欄p0,p1,p*分別表示讀寫頭往當前格子上寫0、1和*字元,移動R或L與它們排在一起,表明了動作,同時也標明瞭先後順序。
    查表基本方法,應從起始狀態x開始,找輸入字元symbol的那一行,經過operations的操作,得到Final m-config欄的新狀態;然後再以新狀態為依據,在左面的m-config欄找到它,然後進行下一次的狀態變換。 

                             表 2 1  圖靈機狀態變化表
              Configuration                    Behaviour                         解釋
        m-config    symbol            operations              Final m-config    
    X(確定運算)            □                   R                 x             x=q0。
                        !                   R                 y             x是開始狀態
                        *                   R                 z    
                         +                   R                 f    
                         0                   R,P*,R,P*,R      erro      Erro是拒絕狀態
                        1                   R,P*,R,P*,R      erro    
    Y(邏輯非)            □                   R,P*,R,P*,R      erro    
                        0                   R,p1                 end             接受
                        1                   R,p0                 end    
                        !                   R,P*,R,P*,R         erro             拒絕
                        *                   R,P*,R,P*,R     erro    
                        +                   R,P*,R,P*,R     erro    
    Z(邏輯與)    □                          R,P*,R,P*,R      erro    
                        0                   R                 Z0             接受
                        1                   R                 Z1    
                        !                   R,P*,R,P*,R     erro             拒絕
                        *                   R,P*,R,P*,R     erro    
                        +                   R,P*,R,P*,R     erro    
    Z0(邏輯與)    □                           R,P*,R,P*,R        erro    
                        0                   R, p0           end             接受
                        1                   R,p0               end    
                        !                  R,P*,R,P*,R     erro             拒絕
                        *                   R,P*,R,P*,R       Erro    
                        +                   R,P*,R,P*,R       Erro    
    Z1(邏輯與)          □                   R,P*,R,P*,R       erro    
                        0                   R, p0           end             接受
                        1                   R,p1               end    
                        !                   R,P*,R,P*,R       erro             拒絕
                        *                   R,P*,R,P*,R     Erro    
                        +                   R,P*,R,P*,R       Erro    
    f(邏輯或)            □                   R,P*,R,P*,R      erro    
                        0                   R               f0             接受
                        1                   R               f1    
                        !                   R,P*,R,P*,R     erro       拒絕
                        *                   R,P*,R,P*,R     Erro    
                        +                   R,P*,R,P*,R       Erro    
    f0(邏輯或)            □                   R,P*,R,P*,R       erro    
                       0                   R, p0           end        接受
                       1                   R,p1               end    
                       !                   R,P*,R,P*,R       erro        拒絕
                       *                   R,P*,R,P*,R       Erro    
                       +                   R,P*,R,P*,R       Erro    
     f1(邏輯或)       □                   R,P*,R,P*,R        erro    
                       0                   R,P1               end        接受
                       1                   R,P1               end    
                       !                   R,P*,R,P*,R       erro    拒絕
                       *                   R,P*,R,P*,R       erro    
                      +                       R,P*,R,P*,R     erro    
    end(正常結束)      □                       R,L,R           x    讀寫頭晃動
                      0                                       erro       拒絕
                      1                                       erro    
                      !                                       erro    
                      *                                       erro    
                      +                                       erro    
    erro              Any way                               erro    qreject停機
    從從表 2 1我們不難看出,影響下一個狀態出現的每一個過程都與原來的狀態q(m-config)和輸入字元資料Symbol(屬於集合Γ中元素)有關。經過operations操作,才到達了新的狀態q'(Final m-config)。顯然這種確定的狀態到狀態間的對映可以用q'=δ(q,γ)來表示,其中q ∈Q,γ∈ Γ。表 2 1就是具體的對映q'=δ(q,γ)的表格表現形式。
    圖靈機的qaccept在這個例子中就是end,而停機qrejet就是erro。
    特別要說明,當我們將Q、Σ和Γ中的元素也都用二進位制數表示的時候,q'=δ(q,γ)就是函式。 

1.1.6. 圖靈機解題例項

    如何通過這張狀態轉換表來進行邏輯運算?
    例如,我們要計算邏輯值1和0的與運算和或運算結果。先可以在帶子上安格寫入:
                □□□□*10□□□□+10□□□□
    這裡要用4個空格區分兩組運算,這是設計的規定。
    第一步:初始化時讀寫頭定在最左邊的位置。開始狀態是x,讀寫頭會讀入的一個空格“□”,組成Q×Γ上的一組“x和 □”,向右查詢,在operations欄有R,這是讓讀寫頭右移一格,移動後進入了下一個狀態x。
    第二步:從表上看,“x和 □”成為查詢條件的得到下一個狀態仍然是x的情況,要重複4次。
    第三步:讀寫頭將讀到“*”,根據表中“x和*”狀態的選擇規定,應現將讀寫頭右移,並決定出下一個狀態“z”。
    第四步:要到左邊狀態列找到“z”,這時讀寫頭會讀入“1”。依據“z 和1”向右面操作欄見到“R”,這是將讀寫頭右移一位的控制操作,並在下一個狀態列找到狀態z1。
    第五步:回頭再到左邊欄找到狀態“z1”,這時讀寫頭將讀到“0”。這要依據“z1和0”一行的操作“R,P0”現將讀寫頭右移一位,然後在空格位置寫上“0”,並進入到“end”狀態。
    第六步:左面的“end”狀態若有讀寫頭讀進空格,則依“end和□”行,可見到操作項是“R,L,R”,這是晃動讀寫頭,表示運算結束,前面寫出的“0”就是1和0做與運算的結果。此次變換得到的狀態是x。
    第七步:End狀態並不是停機狀態,從轉換的新狀態x開始,又返回到圖靈機開始執行的狀態,進入了下一個運算過程。
    以上七步是進行邏輯與運算的過程。邏輯或運算的過程步基本相同。邏輯非運算因為只有一個數參加運算,故步驟少一些。
    從這個邏輯運算的圖靈機來看,停機和拒絕是一個概念,而圖靈機的接受概念與計算得到結果,或計算完成是同一概念。
    此圖靈機對拒絕的問題採用連續輸出兩個星號表示。具體是:如果讀寫頭讀入的是的資料和狀態組成的二元組找不到下一個狀態,那麼會連續列印出“**”,表明帶子上的輸入有誤,並且會進入“erro”狀態,拒絕繼續執行,同時停機。

(未完,待續)

相關文章