《論可計算數及其在判定上的應用》簡單理解

菸草的香味發表於2020-12-12

剛剛拜讀了一本書, 《圖靈的祕密》. 該書介紹了圖靈的論文《論可計算數及其在判定上的應用》, 其指出: 一個擁有鉛筆, 紙和一串明確指令的人類計算者, 可以被看做是一種圖靈機. 那麼圖靈機是什麼呢? 是圖靈為了描述可計算數而引出的一個虛構的可以做一些簡單操作的計算機器. 儘管這個機器很簡單, 但圖靈斷言它再功能上等價與一個進行數學運算的人.

先提個小醒, 文章有些長, 而且還比較枯燥.

當然了, 那些數學證明並不是我關心的, 我關心的是這個圖靈機. 圖靈為了說明他的數學理論, 描述了一臺機器, 而這臺機器, 看過之後發現其實已經算是現代計算機的雛形了, 雖然他的出發點和發明計算機並不沾邊吧. 先簡單介紹一下這臺機器.

這是一個配有一條紙帶的機器, 紙帶由一個個方格隔開, 圖靈只使用其中相見的格子列印資料, 暫且稱之為數字格, 數字格之間的用來輔助計算. 大概就這麼一個紙帶:

image-20201206153828061

而這臺機器對操作的定義由一張狀態表來決定:

狀態 符號 操作 狀態切換

其中每個狀態(原文為: 格局)對應的符號不同, 會執行不同 的操作並切換的對應的下一個狀態. 可以將狀態類比成不同的函式, 再不同的選擇分支下執行不同的邏輯並呼叫不同的函式. 下面給出一些符合和操作的定義:

符號

  • 0/1 : 指定字元
  • Any: 非空的任意符號
  • None: 空
  • 留空: Any 和 None
  • else: 狀態的其他符號都沒有匹配

操作

  • R: 向右移動一格
  • L: 想左移動一格
  • P: 列印字元(P0, 列印0)
  • E: 擦除當前格子內容

OK, 對圖靈這個簡單的機器介紹完畢, 是不是特別簡單. 一起跟著圖靈來看看, 他在這臺機器上都能夠做些什麼操作吧.

列印序列010101...

先給出一格簡單的例子, 來看看這臺機器是如何執行的. 列印序列01010101..., 在前面加一個小數點, 這就是二進位制的1/3了, 而將0和1的位置互換之後, 就是2/3了. 來看看圖靈是如何實現這一功能的.

狀態 符號 操作 狀態切換
b None P0,R c
c None R e
e None P1,R f
f None R b

對了, 圖靈的機器執行都是從狀態b開始的. 讓我們一起跟著狀態表走起來. (圖靈只使用相間的各自來列印數字, 數字格中間的用來輔助計算, 數字格不可更改)

1.展示紙帶的初始狀態

其中紅色方塊標記機器當前的掃描方格.

image-20201206155849929

2.當前狀態: b, 列印0並向右移動一格, 切換狀態: c

image-20201206155953039

3.當前狀態c, 向右移動一格, 切換狀態: e

image-20201206160044268

4.當前狀態e, 列印0並向右移動一格, 切換狀態: f

image-20201206160142677

5.當前狀態 f, 向右移動一格, 切換回狀態: b

image-20201206160224539

此時, 切換回了初始的狀態, 然後周而復始的列印下去. 當然, 上述狀態是可以進行簡化的, 通過當前方格的不同符號, 可以進行不同的操作, 簡化後的狀態表:

狀態 符號 操作 狀態切換
b None P0 b
b 0 R,R,P1 b
b 1 R,R,P0 b

簡單試一下, 簡化後的狀態實現的功能完全一樣.

當然, 這個例子實在太簡單了, 不過為了理解圖靈這臺機器, 還是有必要介紹一下的.

列印序列 001011011101111...

在這個序列中, 1的數量會依次加一, 也就是說要讓這臺機器再這個一維的紙帶上記住前面列印了多少個1. 那麼圖靈是如何做到這一點的呢?

終於, 前面沒有用到的非數字格出場了, 它用來輔助列印.

狀態 符號 操作 狀態切換
b Pa,R,Pa,R,P0,R,R,P0,L,L e
e 1 R,Px,L,L,L e
e 0 q
q Any R,R q
q None P1, L p
p x E,R q
p a R f
p None L,L p
f Any R,R f
f None P0,L,L e

老規矩, 直接來走一遍狀態表, 邊走邊分析.

1. 當前狀態 b, 操作Pa,R,Pa,R,P0,R,R,P0,L,L, 切換狀態e

image-20201206161555876

可以發現, 狀態b在初次執行之後, 就再也沒有出現過了, 所以可以將其形容為初始化的操作.

2.當前狀態e, 符號0,直接切換狀態: q

3.當前狀態q, 符號0, 操作: R,R, 切換狀態q

image-20201206162121027

4.當前狀態q, 符號0, 操作: R,R, 切換狀態: q

image-20201206162238612

5.當前狀態 q, 符號None, 操作: P1, L, 切換狀態p

image-20201206162347231

可以看到, q狀態的操作就是向當前序列的尾部新增一個1.

6.當前狀態 p, 符號None, 操作: L,L, 切換狀態p

這不的操作就是向左移動到第一個不為空的非數字格. 省略一步, 結果:

image-20201206162632707

7.當前狀態p, 符號a, 操作: R, 切換狀態: f

從這裡可以看出來, p狀態其實是充當各個狀態之間的排程員角色的.

image-20201206162756151

8.當前狀態f, 符號0, 操作: R,R, 切換到狀態f

這裡, 可以觀察到, 狀態f的作用是向數字的結尾列印0, 然後左移兩格, 切換到e狀態. 跳過中間步驟:

image-20201206163029975

**9.當前狀態e, 符號1, 操作: R,Px,L,L,L, 切換狀態e. **

簡單分析, e狀態的操作是在連續1的每一個右邊列印x, 之道遇到0, 切換到q狀態

image-20201206163238781

10.當前狀態q, 符號0, 則向尾部新增一個1, 切換狀態p

image-20201206163428097

**11.當前狀態p, 符號None **

通過前面分析, p狀態向左找到第一個不為空的非數字格, 在這裡則是x. 然後會擦除x並切換到q狀態, 既向右列印1. q狀態執行完畢後的紙帶狀態如下:

image-20201206163718013

此時會再次切換回p狀態, 向左找到a, 然後切換到f狀態, 向右列印一個0.

完美, 此時其實已經發現了, 圖靈的方法是在連續1的後面新增x標記, 每個x標記都對應一格末尾的1. 以此來獲得上一次列印1的數量.

至此, 這臺簡單的機器已經能夠記憶一些內容了.

數字遞增

至此, 圖靈這臺機器雖然已經能夠列印一些複雜的內容了, 不過都是一些簡單的重複工作, 還沒有能夠稱之為計算的東西. 為了引出後面的計算, 先來實現一個簡單的數字遞增.

這是一個不符合圖靈約定的機器, 加在這裡只是為了引出計算. 而且他的最高位數字向左列印. 來看一下這個狀態表:

狀態 符號 操作 切換狀態
b None P0 i
i 0 P1 r
i 1 P0,L i
i None P1 r
r None L i
r Any R r

這個簡單的例子就不再展開步驟了, 可以動手試一下, 它確實實現了數字的遞增. 從這裡能夠看出使用二進位制的好處, 如果使用十進位制相同的功能, 則i狀態需要列出0-9始終狀態十種狀態, 而二進位制則只需要兩種.

計算\(\sqrt{2}\)

好了, 論文中首個可以稱為計算的例子來了. \(\sqrt{2}\)是一個無限不迴圈小數.

先來介紹一下在計算\(\sqrt{2}\)時涉及的數學知識. 首先, \(\sqrt{2}\)一定是介於1-2之間的一個小數. 二進位制的\(\sqrt{2}\)前十位是: 1.011. 如何確定下一位是0還是1呢? 方法聽上去很簡單, 先假設下一位是1, 然後讓這個 n 位數與自身相乘, 若結果是2n-1位, 則說明結果小於2, 下一位是1. 若結果是2n位, 則大於2, 下一位是0. 這個很好證明, 可以自己動手算一下.

而一個 n 位數與自身相乘, 需要將其中的每一位與每一位相乘, 共需要計算n*n次. 產生n個區域性乘積, 然後將區域性乘積進行相加得到結果.

而圖靈在計算時, 使用了稍有不同的方法進行乘法計算, 在運算中維護一個過程和, 每一位的相乘結果加到這個過程和中. 當然, 每一個位與位的乘積, 並不是加到過程和的最低位, 而是加到中間的某個位置上.

二進位制的乘法很簡單, 1*1=1, 其他情況都是0. 而乘積加到過程和的哪一位, 如果右起第2位(從0開始)乘以第3位, 則加到結果和的第2+3=5位上. 先說明, 在下方的過程和計算中, 過程和的最低位在左側, 與數字格的順序相反, 應該是為了簡化計算過程吧.

來一起看看圖靈是如何實現這個功能的呢? 這次就不直接上狀態表了, 這個表有點長, 就拆成多個放到分析中了. 如果有感興趣的, 將下方所有狀態合起來就是完整的狀態表了.

狀態 符號 操作 切換狀態
begin None Pa,R,P1 new

為了方便介紹, 我們就不從頭跑一遍了, 太費篇章. 我們就從中間位置開始來看一下他的計算過程, 具體過程可以自己跟著狀態走一遍. 假設現在已經計算出了三位1.01.

其中?標識當前需要計算的下一位, 並沒有實際出現在紙帶上. 每次計算新的一位, 都會呼叫new狀態將掃描格重置到最左邊的數字上:

狀態 符號 操作 切換狀態
new a R mark_digits
new else L new

假設此時, 紙帶的狀態:

image-20201206181041943

現在對各個數字位進行標記.

狀態 符號 操作 切換狀態
mark_digits 0 R,Px,R mark_digits
mark_digits 1 R,Px,R mark_digits
mark_digits None R,Pz,R,R,Pr find_x

很簡單, 在所有已知位後面都標記x. 未知位標記z, 然後在過程和的最低位列印r.

image-20201206181217503

當前狀態: find_x

狀態 符號 操作 切換狀態
find_x x E first_r
find_x a find_digits
find_x else L,L find_x
first_r r R,R last_r
first_r else R,R first_r
last_r r R,R last_r
last_r None Pr,R,R,Pr find_x

r是過程和的最低位, 可以將其看做0. 接下來的步驟, 會將每一個x都對應的列印兩個r. 也就是說, 現在數字一共4位(包括?, 其為1). 然後列印了7個 r (2n-1). 根據之前的推測, 若結果需要新的一位, 則值為0, 否則為1.

image-20201206183028343

當前狀態: find_digits

狀態 符號 操作 切換狀態
find_digits a R,R find_first_digit
find_digits else L,L find_digits
find_first_digit x L found_first_digit
find_first_digit y L found_first_digit
find_first_digit z L found_second_digit
find_first_digit None R,R find_first_digit

如果未知位是1, 那麼過程和7位就夠用了, 否則未知位就是0. 現在, 已經有了都是0的7位過程和, 可以開始做乘法運算了.

現在, 開始準備首次的位與位相乘了. 為了記錄當前是那兩位在做乘法運算, 使用x, y, z進行標記. 其中x標記與y標記做乘法運算, 若自身相乘, 則標記為z. 先通過find_digits 回到第一個非數字格. 然後通過find_first_digit 跳到第一個做乘法運算的數字格. 並根據標記的不同呼叫不同的方法.

image-20201206183908118

當前狀態: found_second_digit

狀態 符號 操作 切換狀態
find_second_digit x L found_second_digit
find_second_digit y L found_second_digit
find_second_digit None R,R find_second_digit
found_first_digit 0 R add_zero
found_first_digit 1 R,R,R find_second_digit
found_second_digit 0 R add_zero
found_second_digit 1 R add_one
found_second_digit None R add_one

這裡可以看到, 若找到的數字是0, 則直接加0, 因為相乘後的結果必是0. 若相乘之後的結果是1, 則向過程和加1.

若找到的第一個數字是1, 則轉換去尋找第二個數字.

image-20201206191441486

當前狀態: add_one

狀態 符號 操作 切換狀態
add_zero r Ps add_finished
add_zero u Pv add_finished
add_zero else R,R add_zero
add_one r Pv add_finished
add_one u Ps, R,R carry
add_one else R,R add_one

雖然給過程和中加0並不會對其值造成改變, 但是不管想其中加入了什麼, 機器都需要對其進行一些維護. 之前說過, 過程和的r表示0, 其實s, t也表示0, 對應的, u, v, w 則表示1. 為什麼需要多個字母來表示同一個數字呢? 是為了下一次加法運算時, 用來標識當前數字已經參與過運算, 應該將結果加到下一位上.

add_zero會將它找到的第一個r標記為s, 或者找到的第一個u標記為v. 然後結束加法操作.

而向過程和中加一, 則需要更多的操作, 畢竟數字已經變了嘛, 而且還需要處理進位. 將找到的第一個r變成v(0變成1), 或者找到的第一個u變成s(1變成0)並同時處理進位.

image-20201206192603902

當前狀態: add_finished

狀態 符號 操作 切換狀態
add_finished a R,R erase_old_x
add_finished else L, L add_finished
erase_old_x x E,L,L print_new_x
erase_old_x z Py,L,L print_new_x
erase_old_x else R,R erase_old_x
print_new_x a R,R erase_old_y
print_new_x y Pz find_digits
print_new_x None Px find_digits
erase_old_y y E,L,L print_new_y
erase_old_y else R,R erase_old_y
print_new_y a R new_digit_is_one
print_new_y else Py,R reset_new_x

此時, 加法結束了, 需要移動x, y, z標記, 來標識下一對需要相乘的數字. 簡單設想一下, 若只有一個z則將其標記為y並將左側標記為x即可. 若一個x一個y, 則將x左移一位, 但是當x到達最高位時, 需要將x重置到最低位, 同時左移y. 當y到達最左側的時候, 計算結束.

image-20201206193501669

當前狀態: find_digits

現在, 計算又回到了最初的狀態, 可以開始進行新一輪的計算了. 這次相乘的結果1*1=1, 再次向過程和中加一. 結果:

image-20201206194001393

繼續執行後, 需要下面幾個狀態(下面為每次回到find_digits狀態的紙帶情況)

狀態 符號 操作 切換狀態
reset_new_x None R,Px flag_result_digits
reset_new_x else R,R reset_new_x
flag_result_digits s Pt,R,R unflag_result_digits
flag_result_digits v Pw,R,R unflag_result_digits
flag_result_digits else R,R flag_result_digits
unflag_result_digits s Pr,R,R unflag_result_digits
unflag_result_digits v Pu,R,R unflag_result_digits
unflag_result_digits else find_digits

image-20201206194354998

image-20201206203909733

可以看到, 當x重置的時候, 下一次位與位相乘的結果需要加到過程和的第二位上, 因此, 需要對過程和的內容做少許修改: 第一個sv變成tw, 剩下的sv變成ru. 為了下一次計算的時候, 能夠將結果加到對應的位置上, 就是下一次相乘結果的相加位要向後一格, 在做加一操作的時候, 只識別r, u, 所以之後的識別符號還需要重置.

操作已經簡單的走了, 是時候將所有狀態放出來了.

狀態 符號 操作 切換狀態
carry r Pu add_finished
carry u Pr,R,R carry
carry None Pu new_digit_is_zero
new_digit_is_zero a R print_zero_digit
new_digit_is_zero else L new_digit_is_zero
print_zero_digit 0 R,E,R print_zero_digit
print_zero_digit 1 R,E,R print_zero_digit
print_zero_digit None P0,R,R,R cleanup
new_digit_is_one a R print_one_digit
new_digit_is_one else L new_digit_is_one
print_one_digit 0 R,E,R print_one_digit
print_one_digit 1 R,E,R print_one_digit
print_one_digit None P1, R,R,R cleanup
cleanup None new
cleanup else E,R,R cleanup

其中carry的操作就是進位操作, 當遇到符號1時, 將其改為0繼續進位, 當遇到符號0的時候, 則改為1, 結束. 若遇到空內容, 說明計算產生第8位了, 則未知位必為0, 直接結束計算.

從上面看到, 還有一種結束情況就是y走到頭了, 這時沒有產生進位, 說明未知位為1.

剩餘的幾個狀態就一帶而過了, 未知位是1, 未知位是0, 以及cleanup清理所有過程和的內容.

整個乘法會以兩種情況結束:

  1. 當進位產生新的位數時, 結束. 未知位是0
  2. y標記下一步走到頭了, 結束. 未知位是1

至此, 已經完成了計算\(\sqrt{2}\)的操作. 這個狀態可以周而復始的一直計算下去. 不再繼續延時了, 感興趣的可以自己按照上面的狀態表算一遍. 看了上面命名的狀態, 有沒有覺得和函式很像呀.

其實其原理並不複雜, 就是進行0和1的簡單嘗試, 然後根據結果的大小來決定後一位是什麼內容. 但我還是被圖靈能夠在一維的紙帶上實現的操作折服了.

方法集

看了上面的內容, 有沒有覺得少了點什麼? 那一個個的不就是函式嘛.而圖靈下一步要做的, 就是要組建一格常用表集, 作為基礎來搭建一些更為複雜的表. 更形象的說法就是封裝函式. 同時, 函式的封裝也方便他後面構建更大的程式. 關於函式的概念就不在贅述了, 天天用的也不少了. 就給出圖靈的表達形式, 大家自然能夠看懂.

回看一下上面的new_digit_is_zeronew_digit_is_one兩個函式, 函式化之後的標識:

狀態 符號 操作 下一個狀態
print_digit(A) 0 R,E,R print_digit(A)
print_digit(A) 1 R,E,R print_digit(A)
print_digit(A) None P(A),R,R,R cleanup

很好理解哈, 就不解釋了. 同時, 其引數也可以傳遞狀態. 將這個基礎表稱之為骨架表. 可以看的出來, 所有的骨架表都可以轉換成不帶引數的形式. 到這裡, 其實和現在的函數語言程式設計思想已經很接近了有木有.

舉個例子:

狀態 符號 操作 下一個狀態
f(S, B, a) α L f1(S, B, a)
else L f(S, B, a)
f1(S, B, a) a S
None R f2(S, B, a)
else R f1(S, B, a)
f2(S, B, a) a S
None R B
else R f1(S, B, a)

其中 α 用來標識開始.

來看一下這個骨架表是做什麼用的? 簡單分析一下:

  • f 函式: 向左找到識別符號α, 然後轉到 f1函式

    • 將掃描格進行定位
  • f1函式: 向右找, 若找到 a, 執行 S 函式, 空格向右轉到 f2函式, 否則繼續向右尋找

    • 找到向右的第一個 a, 執行 S 函式
  • f2函式: 向右找, 若找到 a, 執行 S 俺叔, 空格向右執行 B 函式, 否則向右轉到 f1函式

    • 找到向右的第一個 a, 執行 S 函式
    • 若找到連續兩個空格, 執行 B 函式(與 f1函式配合, 識別連續的兩個空格)

可以看出, f 就是 find, 他會尋找 a(也是引數), 若找到, 執行 S, 沒找到則執行 B.

再來看一個栗子:

狀態 符號 操作 下一個狀態
e1(S) E S
e(S, B, a) f(e1(S), B, a)

看這個骨架表. 函式 e1 的作用是將符號擦除, 然後轉到 S 狀態.

那麼相對的, e 函式的作用是, 向右找到第一個 a, 若找到了則擦除並轉到 S, 若沒有找到, 轉到 B. 同時, 圖靈還允許一個函式同時接收不同個數的引數(想到了什麼? 函式過載)

狀態 符號 操作 下一個狀態
e(B, a) e(e(B, a), B, a)

這個兩個引數的e函式是不是有點皮了. 來分析, 三引數的e, 作用是找到 a 符號並擦除, 然後轉到 S. 再看兩引數的e函式, S 是什麼? 還是他自己, 繼續找一個 a 符號並擦除, 直到擦除調所有的 a.

也就是說, 這個函式實現的功能是, 擦除所有的 a, 然後轉到 B. 從這可以看出, 圖靈的思想對現在的程式設計提供了一定思路, 已經有函式的巢狀呼叫了, 膜拜了.

再來看一些定義的基礎庫, 來幫助理解圖靈的這個概念.

找到出現的最後一格 a

函式 f 從左向右查詢, 函式 g 從右向左找.

狀態 符號 操作 下一個狀態
g(S) Any R g(S)
None R g1(S)
g1(s) Any R g(S)
None S
g1(S, a) a s
else L g1(S, a)
g(S, a) g(g1(S, a))

其中單引數的函式 g 和單引數的函式 g1配合, 將掃描格移到最右側.

在結尾列印

狀態 符號 操作 下一個狀態
pe(S, b) f(pe1(S, b), S, α)
pe1(S, b) Any R,R pe(S, b)
None Pb S

其中, 這個函式有一個假設, 就是最左側有兩個連續α, f 函式先將掃描格移動到最左側α, 然後右移一格開始尋找, 這時當前格就是α.

在結尾列印兩個字元

狀態 下一個狀態
pe2(S, a, b) pe(pe(S, b), a)

直接先在結尾列印 a, 然後在在結尾列印 b

增強 find 函式

f 函式在找到所需字元後, 將掃描格向左或向右移動.

狀態 操作 下一個狀態
l(S) L S
r(S) R S
fl(S, B, a) f(l(S), B, a)
fr(S, B, a) f(r(S), B, a)

複製字元

找到用 a 標記的字元, 複製到結尾. 然後呼叫 S 函式.

狀態 符號 下一個狀態
c(S, B, a) fl(c1(S), B, a)
c1(S) β pe(S, β)

這裡有一個特殊的地方, c1函式中, 符號β表示掃描到的字元. 當然也可以將不同的字元進行排列(可能只有0或1).

複製並擦除

狀態 下一個狀態
ce(S, B, a) c(e(S, B, a), B, a)
ce(B, a) ce(ce(B, a), B, a)

其中三引數的ce, 會找到 a 標記的符號, 並複製到結尾. 然後呼叫e擦除 a 標記. 擦除後執行第一個引數的狀態. 而在兩引數的ce中, 傳遞過期的第一個引數是它自己. 就是說, 兩引數的ce會將所有 a 標記的符號複製, 同時擦除 a 標記, 最終轉到 B.

(在之前列印'001011011101111...'的例子中, 就可以使用這個函式對1進行復制)

看到這裡已經發現了, 圖靈機令人咋舌的效率問題. 為了執行以此複製並擦除, 函式 c 會先呼叫 f 遍歷一遍(f 函式會先回到開頭的位置)進行復制操作, 然後再呼叫 f 進行遍歷並將其擦除. 而複製擦除下一個字元, 又要重複這些操作. 如果在第一次找到的時候, 就順手擦除, 然後再進行復制, 豈不更好.

不過, 想必圖靈在當時並沒有考慮效率的問題, 或者說他並不關心效率的好壞, 畢竟連這臺機器都是想象出來的. 現在, 圖靈已經可以構建一格函式庫了, 類似與現在的系統庫, 想必能夠構造更為強大的機器了.

數字化

接下來, 圖靈對他的表格進行了重新定義. 他先是證明了所有狀態都可以拆解成如下所示的三個狀態:

狀態 符號 操作 符號 標識
qi Sj PSk, L qm N1
qi Sj PSk, R qm N2
qi Sj PSk qm N3

其中 Sk 用來表示符號. 規定:

  • S0 : 表示空格
  • S1 : 表示0
  • S2 : 表示1
  • 其他: 一些自定義符號

其中的操作是:

  • N1 : 列印並左移
  • N2 : 列印並右移
  • N3 : 列印

疑問, 改成這樣真的能夠表示之前的所有操作麼? 舉例一下 :

  • 擦除操作: 既列印空格. PS0
  • 左移操作: 既列印格子原本的符號.

而之前的一些較長的操作, 通過拆解也可以拆成這樣的基本形式. 然後, 圖靈定義了這麼一格五元組:

qiSjSkLqm 用來直接表示上方的 N1 . 無用說也能和上面的表格對上吧. 有沒有想到圖靈要做什麼? 這裡每一個五元組, 都對應一個操作, 如果將多個五元組連起來, 並用分號隔開, 是不是就能完整的描述一個程式了.

至此, 他的狀態表已經經過了一次轉換, 變成了下標的形式. 接下來, 圖靈要把下標去掉. 替換:

  • qi -> D 後面 i 個 A
  • Sj -> D 後面 j 個 C

如此一來, 他的機器就只剩下以下幾個字元: D, A, C, L, R, N, ;. 其中N表示不移動. 圖靈將這樣的描述稱為標準描述.

再然後, 圖靈將僅存的這7個字元, 用1-7的數字來表示. 既: 1(A), 2(C), 3(D), 4(L), 5(R), 6(N), 7(?. 那麼, 將得到一個完全由數字組成的完成程式. 而這些數字連起來, 就是一個比較大的整數, 也就是說, 圖靈用一個整數來完全表示了他的機器. 這個數字被圖靈稱為描述數.

也就是說, 一個描述數可以唯一確定一個程式, 而一個程式可以對應多個描述數(因為狀態的順序是可以隨意更換的). 同時, 這也說明通過列舉所有的整數, 可以得到所有可能的計算序列.

其實與現在的程式有著異曲同工之妙, 現在的程式也不過是一串二進位制的數字.

可程式設計通用機

接下來, 圖靈描述了一個可程式設計的通用機器, 將程式1的描述數放到紙帶的開頭, 機器 A 通過讀取並完全復刻所有格局來實現程式1的功能. 這臺機器可以通過讀取不同的輸入紙帶, 來實現不同程式的功能.

同時, 圖靈證明了這樣一個機器的可行性. 現在, 假設需要執行的程式是之前的交替列印0和1的程式, 其狀態表如下:

狀態 符號 操作 下一個狀態
b None P0 b
0 R,R,P1 b
1 R,R,P0 b

轉換成通用格局之後:

狀態 符號 操作 下一個狀態
q1 S0 PS1,R q2
q2 S0 PS2,R q1

很簡單, 列印0然後列印1, 交替進行. 將通用格局轉換成可描述符號:

  • q1S0S1Rq2: DADDCRDAA
  • q2S0S2Rq1: DAADDCCRDA

輸入紙帶如下所示(一行顯示不下, 但他還是一維紙帶哦):

image-20201212171922404

每一條指令由五個連續部分組成:

  1. D 接 n 個 A: 表示狀態, 最少由一個 A
  2. D 接 n 個 C: 表示識別符號
  3. D 接 n 個 C: 表示在掃描格列印的符號
  4. L/R/N: 表示掃描頭的移動方向
  5. D 接 n 個 A: 表示下一個切換的狀態.

接下來的證明過程, 就有些超出我的理解了, 感興趣的朋友可以自行鑽研一下, 我是看了好久, 也沒搞懂.

至此, 圖靈的這臺機器, 其實已經有了現代計算機的雛形了. 而我, 也被這幾十年前偉大的思想折服了. 牛批...

同時, 圖靈的論文後面還使用這臺通用機器進行了一些證明, 不過, 那並不是我所關心的內容.

相關文章