之前備考時(shí)無(wú)意間看到這篇文章【康托爾、哥德?tīng)枴D靈——永恒的金色對(duì)角線(xiàn)】,令我驚為天人。劉未鵬從一系列深?yuàn)W的理論背后找到了一條線(xiàn),用一個(gè)至為簡(jiǎn)單而又至為深刻的數(shù)學(xué)方法將其串聯(lián)起來(lái),然我們看到了最純粹的數(shù)學(xué)之美!現(xiàn)在終于有時(shí)間能夠靜下心來(lái)重新看一遍,順便寫(xiě)一篇讀書(shū)筆記方便交流與理解。
那么圖靈的停機(jī)問(wèn)題(The Halting Problem)、Y Combinator、著名的哥德?tīng)柌煌陚涠ɡ怼⒘_素悖論,究竟是如何由康托爾的天才手法串聯(lián)起來(lái)的?不要急,同學(xué)你先坐下,接下來(lái)我們將細(xì)細(xì)道來(lái)。
首先,我們要從圖靈著名的停機(jī)問(wèn)題說(shuō)起,一來(lái)它相對(duì)來(lái)說(shuō)是我們要說(shuō)的幾個(gè)定理當(dāng)中最簡(jiǎn)單的,二來(lái)它也最貼近程序員。
1. 停機(jī)問(wèn)題(The Halting Proble)
不存在這樣一個(gè)程序(算法),它能夠計(jì)算任何程序(算法)在給定輸入上是否會(huì)結(jié)束(停機(jī))。
證明如下:
假如我們有一天真的做出了這個(gè)極度聰明的算法——神之算法(God_algo),你只要給它一段程序program
,再給它這段程序的輸入input
,它就能告訴你這段程序在這個(gè)輸入的情況下會(huì)不會(huì)結(jié)束(停機(jī))。神之算法如下:
bool God_algo(char* program, char* input)
{
if(<program> halts on <input>)
return true;
return false;
}
emmm......這里我們假設(shè) if 的判斷語(yǔ)句里面是你天才思考的結(jié)晶,它能夠像上帝一樣洞察一切程序的宿命 - -!
然后,我們可以從它導(dǎo)出一個(gè)新的算法:
bool Satan_algo(char* program)
{
if( God_algo(program, program) ){
while(1); // loop forever!
return false; // can never get here!
}
else
return true;
}
正如它的名字所暗示的那樣,這個(gè)撒旦算法便是一切邪惡的根源了!!!
為什么這么說(shuō)呢?我們來(lái)看看將撒旦算法運(yùn)用到它自身時(shí)會(huì)發(fā)生什么呢?
Satan_algo(Satan_algo);
我們來(lái)分析一下這行簡(jiǎn)單的調(diào)用:
顯然,Satan_algo(Satan_algo)這個(gè)調(diào)用要么能夠運(yùn)行結(jié)束返回(停機(jī)),要么不能返回(loop forever)。
如果它能夠結(jié)束,那么Santa_algo算法里面的那個(gè)if判斷就會(huì)成立(因?yàn)镚od_algo(Santa_algo,Santa_algo)將會(huì)返回true),從而程序便進(jìn)入那個(gè)包含一個(gè)無(wú)窮循環(huán)while(1);的if分支,于是這個(gè)Satan_algo(Satan_algo)調(diào)用便永遠(yuǎn)不會(huì)返回(結(jié)束)了。
而如果Satan_algo(Satan_algo)不能結(jié)束(停機(jī))呢,則if判斷就會(huì)失敗,從而選擇另一個(gè)if分支并返回true,即Satan_algo(Satan_algo)又能夠返回(停機(jī))。
總之,我們有:
Satan_algo(Satan_algo)能夠停機(jī) => 它不能停機(jī)
Satan_algo(Satan_algo)不能停機(jī) => 它能夠停機(jī)
所以它停也不是,不停也不是。左右矛盾。
于是,我們的假設(shè),即神之算法的存在性,便不成立了。
相信每個(gè)程序員都能夠看懂這個(gè)證明,但是圖靈到底是如何想出這一絕妙證明呢?畢竟,這個(gè)證明一點(diǎn)都不顯然 - -!
2. Y Combinator 與 Lambda Calculus
接下來(lái),我們先跳過(guò)停機(jī)問(wèn)題,繼續(xù)來(lái)了解一下由跟圖靈機(jī)理論等價(jià)的lambda算子發(fā)展出來(lái)的另一個(gè)平行的語(yǔ)言世界——函數(shù)式編程語(yǔ)言的世界。
Y Combinator,這個(gè)由師從希爾伯特的著名邏輯學(xué)家Haskell B.Curry“發(fā)明”出來(lái)的組合算子仿佛有種神奇的魔力,它能夠算出給定lambda表達(dá)式(函數(shù))的不動(dòng)點(diǎn),從而使得遞歸成為可能。
為了理解Y Combinator,我們必須要了解一點(diǎn)點(diǎn)lambda算子理論的基礎(chǔ)知識(shí)。
先來(lái)看一下lambda表達(dá)式的基本語(yǔ)法(BNF):
<expr> ::= <identifier>
<expr> ::= lambda <identifier-list>. <expr>
<expr> ::= (<expr> <expr>)
前兩條語(yǔ)法用于生成lambda表達(dá)式(lambda函數(shù)),如:
lambda x y. x + y
這是一個(gè)匿名的加法函數(shù),它接受兩個(gè)參數(shù),返回兩值相加的結(jié)果。當(dāng)然,這里我們?yōu)榱朔奖闫鹨?jiàn)賦予了lambda函數(shù)直觀(guān)的計(jì)算意義,而實(shí)際上lambda calculus里面一切都只不過(guò)是文本替換,有點(diǎn)像C語(yǔ)言的宏。并且這里的“+”我們假設(shè)已經(jīng)是一個(gè)具有原子語(yǔ)義的運(yùn)算符[6],此外,為了方便我們使用了中綴表達(dá)(按照l(shuí)ambda calculus系統(tǒng)的語(yǔ)法實(shí)際上應(yīng)該寫(xiě)成“(+ x y)”才對(duì)——參考第三條語(yǔ)法)。
那么,函數(shù)定義出來(lái)了,怎么使用呢?最后一條規(guī)則就是用來(lái)調(diào)用一個(gè)lambda函數(shù)的:
((lambda x y. x + y) 2 3)
以上這一行就是把剛才定義的加法函數(shù)運(yùn)用到2和3上(這個(gè)調(diào)用語(yǔ)法形式跟命令式語(yǔ)言(imperative language)慣用的調(diào)用形式有點(diǎn)區(qū)別,后者是“f(x, y)”,而這里是“(f x y)”,不過(guò)好在順序沒(méi)變:) )。為了表達(dá)簡(jiǎn)潔一點(diǎn),我們可以給(lambda x y. x + y)起一個(gè)名字,像這樣:
let Add = (lambda x y. x + y)
這樣我們便可以使用Add來(lái)表示該lambda函數(shù)了:
(Add 2 3)
不過(guò)還是為了方便起見(jiàn),后面調(diào)用的時(shí)候一般用“Add(2, 3)”,即我們熟悉的形式。
有了語(yǔ)法規(guī)則之后,我們便可以看一看這個(gè)語(yǔ)言系統(tǒng)的兩條簡(jiǎn)單至極的公理了:
Alpha轉(zhuǎn)換公理:例如,“l(fā)ambda x y. x + y”轉(zhuǎn)換為“l(fā)ambda a b. a + b”。換句話(huà)說(shuō),函數(shù)的參數(shù)起什么名字沒(méi)有關(guān)系,可以隨意替換,只要函數(shù)體里面對(duì)參數(shù)的使用的地方也同時(shí)注意相應(yīng)替換掉就是了。
Beta轉(zhuǎn)換公理:例如,“(lambda x y. x + y) 2 3”轉(zhuǎn)換為“2 + 3”。這個(gè)就更簡(jiǎn)單了,也就是說(shuō),當(dāng)把一個(gè)lambda函數(shù)用到參數(shù)身上時(shí),只需用實(shí)際的參數(shù)來(lái)替換掉其函數(shù)體中的相應(yīng)變量即可。
就這些。是不是感覺(jué)有點(diǎn)太簡(jiǎn)單了?但事實(shí)就是如此,lambda算子系統(tǒng)從根本上其實(shí)就這些東西,然而你卻能夠從這幾個(gè)簡(jiǎn)單的規(guī)則中推演出神奇無(wú)比的Y combinator來(lái)。我們這就開(kāi)始!
敏銳的你可能會(huì)發(fā)現(xiàn),就以上這兩條公理,我們的lambda語(yǔ)言中無(wú)法表示遞歸函數(shù)。
為什么呢?假設(shè)我們要計(jì)算階乘,遞歸描述肯定像這樣:
f(n):
if n == 0 return 1
return n*f(n-1)
當(dāng)然,上面這個(gè)程序是假定n為正整數(shù)。這個(gè)程序顯示了一個(gè)特點(diǎn),f 在定義的過(guò)程中用到了它自身。那么如何在lambda算子系統(tǒng)中表達(dá)這一函數(shù)呢?理所當(dāng)然的想法如下:
lambda n. If_Else n==0 1 n*<self>(n-1)
當(dāng)然,上面的程序假定了 If_Else 是一個(gè)已經(jīng)定義好的三元操作符(你可以想象C的? :
操作符,后面跟的三個(gè)參數(shù)分別是判斷條件、成功后求值的表達(dá)式、失敗后求值的表達(dá)式。那么很顯然,這個(gè)定義里面有一個(gè)地方?jīng)]法解決,那就是<self>那個(gè)地方我們應(yīng)該填入什么呢?很顯然,熟悉C這類(lèi)命令式語(yǔ)言的人都知道應(yīng)該填入這個(gè)函數(shù)本身的名字,然而lambda算子系統(tǒng)里面的lambda表達(dá)式(或稱(chēng)函數(shù))是沒(méi)有名字的。
怎么辦?難道就沒(méi)有辦法實(shí)現(xiàn)遞歸了?或者說(shuō),丘齊做出的這個(gè)lambda算子系統(tǒng)里面根本沒(méi)法實(shí)現(xiàn)遞歸從而在計(jì)算能力上面有重大的缺陷?顯然不是。馬上你就會(huì)看到Y(jié) combinator是如何把一個(gè)看上去非遞歸的lambda表達(dá)式像變魔術(shù)那樣變成一個(gè)遞歸版本的。在成功之前我們?cè)偈∫淮?/strong>,注意下面的嘗試:
let F = lambda n. IF_Else n==0 1 n*F(n-1)
看上去不錯(cuò),是嗎?可惜還是不行。因?yàn)閘et F只是起到一個(gè)語(yǔ)法糖的作用,在它所代表的lambda表達(dá)式還沒(méi)有完全定義出來(lái)之前你是不可以使用F這個(gè)名字的。更何況實(shí)際上丘齊當(dāng)初的lambda算子系統(tǒng)里面也并沒(méi)有這個(gè)語(yǔ)法元素,這只是剛才為了簡(jiǎn)化代碼而引入的語(yǔ)法糖。當(dāng)然,了解這個(gè)let語(yǔ)句還是有意義的,后面還會(huì)用到。
在上面幾次失敗的嘗試之后,我們是不是就一籌莫展了呢?別忘了軟件工程里面的一條黃金定律:“任何問(wèn)題都可以通過(guò)增加一個(gè)間接層來(lái)解決”。不妨把它沿用到我們面臨的遞歸問(wèn)題上:沒(méi)錯(cuò),我們的確沒(méi)辦法在一個(gè)lambda函數(shù)的定義里面直接(按名字)來(lái)調(diào)用其自身。但是,可不可以間接調(diào)用呢?
我們回顧一下剛才不成功的定義:
lambda n. If_Else n==0 1 n*<self>(n-1)
現(xiàn)在<self>處不是缺少“這個(gè)函數(shù)自身”嘛,既然不能直接填入“這個(gè)函數(shù)自身”,我們可以增加一個(gè)參數(shù),也就是說(shuō),把<self>參數(shù)化:
lambda self n. If_Else n==0 1 n*self(n-1)
上面這個(gè)lambda算子總是合法定義了吧。現(xiàn)在,我們調(diào)用這個(gè)函數(shù)的時(shí)候,只要加傳一個(gè)參數(shù)self,這個(gè)參數(shù)不是別人,正是這個(gè)函數(shù)自身。還是為了簡(jiǎn)單起見(jiàn),我們用let語(yǔ)句來(lái)給上面這個(gè)函數(shù)起個(gè)別名:
let P = lambda self n. If_Else n==0 1 n*self(n-1)
我們這樣調(diào)用,比如說(shuō)我們要計(jì)算3的階乘:
P(P, 3)
也就是說(shuō),把P自己作為P的第一個(gè)參數(shù)(注意,調(diào)用的時(shí)候P已經(jīng)定義完畢了,所以我們當(dāng)然可以使用它的名字了)。這樣一來(lái),P里面的self處不就等于是P本身了嗎?自身調(diào)用自身,遞歸!
可惜這只是個(gè)美好的設(shè)想,還差一點(diǎn)點(diǎn)。我們分析一下P(P, 3)這個(gè)調(diào)用。利用前面講的Beta轉(zhuǎn)換規(guī)則,這個(gè)函數(shù)調(diào)用展開(kāi)其實(shí)就是:
IF_Else n==0 1 n*P(n-1)
看出問(wèn)題了嗎?這里的P(n-1)雖然調(diào)用到了P,然而只給出了一個(gè)參數(shù);而從P的定義來(lái)看,它是需要兩個(gè)參數(shù)的(分別為self和n)!也就是說(shuō),為了讓P(n-1)變成良好的調(diào)用,我們得加一個(gè)參數(shù)才行,所以我們得稍微修改一下P的定義:
let P = lambda self n. If_Else n==0 1 n*self(self, n-1)
請(qǐng)注意,我們?cè)赑的函數(shù)體內(nèi)調(diào)用self的時(shí)候增加了一個(gè)參數(shù)。現(xiàn)在當(dāng)我們調(diào)用P(P, 3)的時(shí)候,展開(kāi)就變成了:
IF_Else 3==0 1 3*P(P, 3-1)
而P(P, 3-1)
是對(duì)P合法的遞歸調(diào)用。這次我們真的成功了!
然而,看看我們的P的定義,是不是很丑陋?n*self(self, n-1)
?什么玩意?為什么要多出一個(gè)多余的self?我們想起我們一開(kāi)始定義的那個(gè)失敗的P,雖然行不通,但最初的努力往往是大腦最先想到的最直觀(guān)的做法,我們來(lái)回顧一下:
let P = lambda self n. If_Else n==0 1 n*self(n-1)
這個(gè)P的函數(shù)體就非常清晰,沒(méi)有冗余成分,雖然參數(shù)列表里面多出一個(gè)self,但我們其實(shí)根本不用管它,看函數(shù)體就行了,self這個(gè)名字已經(jīng)可以說(shuō)明一切了對(duì)不對(duì)?但很可惜這個(gè)函數(shù)不能用。我們?cè)賮?lái)回想一下為什么不能用呢?因?yàn)楫?dāng)你調(diào)用P(P, n)的時(shí)候,里面的self(n-1)會(huì)展開(kāi)為P(n-1)而P是需要兩個(gè)參數(shù)的。唉,要是這里的self是一個(gè)“真正”的,只需要一個(gè)參數(shù)的遞歸階乘函數(shù),那該多好啊。為什么不呢?干脆我們假設(shè)出一個(gè)“真正”的遞歸階乘函數(shù):
power(n):
if(n==0) return 1;
return n*power(n-1);
但是,前面不是說(shuō)過(guò)了,這個(gè)理想的版本無(wú)法在lambda算子系統(tǒng)中定義出來(lái)嗎(由于lambda函數(shù)都是沒(méi)名字的,無(wú)法自己內(nèi)部調(diào)用自己)?不急,我們并不需要它被定義出來(lái),我們只需要在頭腦中“假設(shè)”它以“某種”方式被定義出來(lái)了,現(xiàn)在我們把這個(gè)真正完美的power傳給P,這樣:
P(power, 3)
注意它跟P(P, 3)的不同,P(P, 3)我們傳遞的是一個(gè)有缺陷的P為參數(shù)。而P(power, 3)我們則是傳遞的一個(gè)真正的遞歸函數(shù)power。我們?cè)囍归_(kāi)P(power, 3):
IF_Else 3==0 1 3*power(3-1)
發(fā)生了什么??power(3-1)將會(huì)計(jì)算出2的階乘(別忘了,power是我們?cè)O(shè)想的完美遞歸函數(shù)),所以這個(gè)式子將會(huì)忠實(shí)地計(jì)算出3的階乘!
回想一下我們是怎么完成這項(xiàng)任務(wù)的:我們?cè)O(shè)想了一個(gè)以某種方式構(gòu)造出來(lái)的完美的能夠內(nèi)部自己調(diào)用自己的遞歸階乘函數(shù)power,我們發(fā)現(xiàn)把這個(gè)power傳給P的話(huà),P(power, n)的展開(kāi)式就是真正的遞歸計(jì)算n階乘的代碼了。
你可能要說(shuō):廢話(huà)!都有了power了我們還要費(fèi)那事把它傳給P來(lái)個(gè)P(power, n)干嘛?直接power(n)不就得了?! 別急,之所以設(shè)想出這個(gè)power只是為了引入不動(dòng)點(diǎn)的概念,而不動(dòng)點(diǎn)的概念將會(huì)帶領(lǐng)我們發(fā)現(xiàn)Y combinator。
什么是不動(dòng)點(diǎn)?一點(diǎn)都不神秘。讓我們考慮剛才的power與P之間的關(guān)系。一個(gè)是真正可遞歸的函數(shù),一個(gè)呢,則是以一個(gè)額外的self參數(shù)來(lái)試圖實(shí)現(xiàn)遞歸的偽遞歸函數(shù),我們已經(jīng)看到了把power交給P為參數(shù)發(fā)生了什么,對(duì)吧?不,似乎還沒(méi)有,我們只是看到了,“把power加上一個(gè)n一起交給P為參數(shù)”能夠?qū)崿F(xiàn)真正的遞歸。現(xiàn)在我們想考慮power跟P之間的關(guān)系,直接把power交給P如何?
P(power)
這是什么?這叫函數(shù)的部分求值(partial evaluation)。換句話(huà)說(shuō),第一個(gè)參數(shù)是給出來(lái)了,但第二個(gè)參數(shù)還懸在那里,等待給出。那么,光給一個(gè)參數(shù)得到的是什么呢?是“還剩一個(gè)參數(shù)待給的一個(gè)新的函數(shù)”。其實(shí)也很簡(jiǎn)單,只要按照Beta轉(zhuǎn)換規(guī)則做就是了,把P的函數(shù)體里面的self出現(xiàn)處皆替換為power就可以了。我們得到:
IF_Else n==0 1 n*power(n-1)
當(dāng)然,這個(gè)式子里面還有一個(gè)變量沒(méi)有綁定,那就是n,所以這個(gè)式子還不能求值,你需要給它一個(gè)n才能具體求值,對(duì)吧。這么說(shuō),這可不就是一個(gè)以n為參數(shù)的函數(shù)么?實(shí)際上就是的。在lambda算子系統(tǒng)里面,如果給一個(gè)lambda函數(shù)的參數(shù)不足,則得到的就是一個(gè)新的lambda函數(shù),這個(gè)新的lambda函數(shù)所接受的參數(shù)也就是你尚未給出的那些參數(shù)。換句話(huà)來(lái)說(shuō),調(diào)用一個(gè)lambda函數(shù)可以分若干步來(lái)進(jìn)行,每次只給出一部分參數(shù),而只有等所有參數(shù)都給齊了,函數(shù)的求值結(jié)果才能出來(lái),否則你得到的就是一個(gè)“中間函數(shù)”。
那么,這跟不動(dòng)點(diǎn)定理有什么關(guān)系?關(guān)系大了,剛才不是說(shuō)了,P(power)返回的是一個(gè)新的“中間函數(shù)”嘛?這個(gè)“中間函數(shù)”的函數(shù)體我們剛才已經(jīng)看到了,就是簡(jiǎn)單地展開(kāi)P(power)而已,回顧一遍:
IF_Else n==0 1 n*power(n-1)
我們已經(jīng)知道,這是個(gè)函數(shù),參數(shù)n待定。因此我們不妨給它加上一個(gè)“l(fā)ambda n”的帽子,這樣好看一點(diǎn):
lambda n. IF_Else n==0 1 n*power(n-1)
這是什么呢?這可不就是power本身的定義?(當(dāng)然,如果我們能夠定義power的話(huà))。不信我們看看power如果能夠定義出來(lái)像什么樣子:
let power = lambda n. IF_Else n==0 1 n*power(n-1)
一模一樣!也就是說(shuō),P(power)展開(kāi)后跟power是一樣的。即:
P(power) = power
以上就是所謂的不動(dòng)點(diǎn)。即對(duì)于函數(shù)P來(lái)說(shuō)power是這樣一個(gè)“點(diǎn)”:當(dāng)把P用到power身上的時(shí)候,得到的結(jié)果仍然還是power,也就是說(shuō),power這個(gè)“點(diǎn)”在P的作用下是“不動(dòng)”的。
可惜的是,這一切居然都是建立在一個(gè)不存在的power的基礎(chǔ)上的,又有什么用呢?可別過(guò)早提“不存在”這個(gè)詞,你覺(jué)得一樣?xùn)|西不存在或許只是你沒(méi)有找到使它存在的正確方法。我們已經(jīng)看到power是跟P有著密切聯(lián)系的。密切到什么程度呢?對(duì)于偽遞歸的P,存在一個(gè)power,滿(mǎn)足P(power)=power。注意,這里所說(shuō)的“偽遞歸”的P,是指這樣的形式:
let P = lambda self n. If_Else n==0 1 n*self(n-1) // 注意,不是self(self,n-1)
一般化的描述就是,對(duì)任一偽遞歸F(回想一下偽遞歸的F如何得到——是我們?yōu)榱私鉀Qlambda函數(shù)不能引用自身的問(wèn)題,于是給理想的f加一個(gè)self參數(shù)從而得到的),必存在一個(gè)理想f(F就是從這個(gè)理想f演變而來(lái)的),滿(mǎn)足F(f) = f。
那么,現(xiàn)在的問(wèn)題就歸結(jié)為如何針對(duì)F找到它的f了。根據(jù)F和f之間的密切聯(lián)系(F就比f(wàn)多出一個(gè)self參數(shù)而已),我們可以從F得出f嗎?假設(shè)我們可以(又是假設(shè)),也就是說(shuō)假設(shè)我們找到了一根魔棒,把它朝任意一個(gè)偽遞歸的F一揮,眼前一花,它就變成了真正的f了。這根魔棒如果存在的話(huà),它具有什么性質(zhì)?我們假設(shè)這個(gè)神奇的函數(shù)叫做Y,把Y用到任何偽遞歸的函數(shù)F上就能夠得到真正的f,也就是說(shuō):
Y(F) = f
結(jié)合上面的F(f) = f,我們得到:
Y(F) = f = F(f) = F(Y(F))
也就是說(shuō),Y具有性質(zhì):
Y(F) = F(Y(F))
性質(zhì)倒是找出來(lái)了,怎么構(gòu)造出這個(gè)Y卻又成了難題。一個(gè)辦法就是使用抽象法,這是從工程學(xué)的思想的角度,也就是通過(guò)不斷迭代、重構(gòu),最終找到問(wèn)題的解。然而對(duì)于這里的Y combinator,接近問(wèn)題解的過(guò)程卻顯得復(fù)雜而費(fèi)力,甚至過(guò)程中的有些點(diǎn)上的思維跳躍有點(diǎn)如羚羊掛角無(wú)跡可尋。然而,在這整個(gè)Y combinator介紹完了之后我們將會(huì)介紹著名的哥德?tīng)柌煌陚湫远ɡ恚缓笪覀兙蜁?huì)發(fā)現(xiàn),通過(guò)哥德?tīng)柌煌陚湫远ɡ碜C明中的一個(gè)核心構(gòu)造式,只需一步自然的推導(dǎo)就能得出我們的Y combinator。
讓我們先來(lái)看一看Y combinator的費(fèi)力而復(fù)雜的工程學(xué)構(gòu)造法:
我們?cè)俅位仡櫼幌履莻€(gè)偽遞歸的求階乘函數(shù):
let P = lambda self n. If_Else n==0 1 n*self(n-1)
我們的目標(biāo)是找出P的不動(dòng)點(diǎn)power,根據(jù)不動(dòng)點(diǎn)的性質(zhì),只要把power傳給P,即P(power),便能夠得到真正的遞歸函數(shù)了。
現(xiàn)在,關(guān)鍵的地方到了,由于:
power = P(power) // 不動(dòng)點(diǎn)原理
這就意味著,power作為一個(gè)函數(shù)(lambda calculus里面一切都是函數(shù)),它是自己調(diào)用了自己的。那么,我們?nèi)绾螌?shí)現(xiàn)這樣一個(gè)能夠自己調(diào)用自己的power呢?回顧我們當(dāng)初成功的一次嘗試,要實(shí)現(xiàn)遞歸,我們是通過(guò)增加一個(gè)間接層來(lái)進(jìn)行的:
let power_gen = lambda self. P(self(self))
還記得self(self)這個(gè)形式嗎?我們?cè)诔晒?shí)現(xiàn)出求階乘遞歸函數(shù)的時(shí)候不就是這么做的?那么對(duì)于現(xiàn)在這個(gè)power_gen,怎么遞歸調(diào)用?
power_gen(power_gen)
不明白的話(huà)可以回顧一下前面我們調(diào)用P(P, n)的地方。這里power_gen(power_gen)展開(kāi)后得到的是什么呢?我們根據(jù)剛才power_gen的定義展開(kāi)看一看,原來(lái)是:
P(power_gen(power_gen))
看到了嗎?也就是說(shuō):
power_gen(power_gen) => P(power_gen(power_gen))
現(xiàn)在,我們把power_gen(power_gen)當(dāng)成整體看,不妨令為power,就看得更清楚了:
power => P(power)
這不正是我們要的答案么?
OK,我們總結(jié)一下:對(duì)于給定的P,只要構(gòu)造出一個(gè)相應(yīng)的power_gen如下:
let power_gen = lambda self. P(self(self))
我們就會(huì)發(fā)現(xiàn),power_gen(power_gen)這個(gè)調(diào)用展開(kāi)后正是P(power_gen(power_gen))。也就是說(shuō),我們的power_gen(power_gen)就是我們苦苦尋找的不動(dòng)點(diǎn)了!
現(xiàn)在我們終于可以鑄造我們的Y Combinator了,Y Combinator只要生成一個(gè)形如power_gen的lambda函數(shù)然后把它應(yīng)用到自身,就大功告成:
let Y = lambda F.
let f_gen = lambda self. F(self(self))
return f_gen(f_gen)
稍微解釋一下,Y是一個(gè)lambda函數(shù),它接受一個(gè)偽遞歸F,在內(nèi)部生成一個(gè)f_gen(還記得我們剛才看到的power_gen吧),然后把f_gen應(yīng)用到它自身(記得power_gen(power_gen)吧),得到的這個(gè)f_gen(f_gen)也就是F的不動(dòng)點(diǎn)了(因?yàn)閒_gen(f_gen) = F(f_gen(f_gen))),而根據(jù)不動(dòng)點(diǎn)的性質(zhì),F(xiàn)的不動(dòng)點(diǎn)也就是那個(gè)對(duì)應(yīng)于F的真正的遞歸函數(shù)!
如果你還覺(jué)得不相信,我們稍微展開(kāi)一下看看,還是拿階乘函數(shù)說(shuō)事,首先我們定義階乘函數(shù)的偽遞歸版本:
let Pwr = lambda self n. If_Else n==0 1 n*self(n-1)
讓我們把這個(gè)Pwr交給Y,看會(huì)發(fā)生什么(根據(jù)剛才Y的定義展開(kāi)吧):
Y(Pwr) =>
let f_gen = lambda self. Pwr(self(self))
return f_gen(f_gen)
Y(Pwr)的求值結(jié)果就是里面返回的那個(gè)f_gen(f_gen),我們?cè)俑鶕?jù)f_gen的定義展開(kāi)f_gen(f_gen),得到:
Pwr(f_gen(f_gen))
也就是說(shuō):
Y(Pwr) => f_gen(f_gen) => Pwr(f_gen(f_gen))
我們來(lái)看看得到的這個(gè)Pwr(f_gen(f_gen))到底是不是真有遞歸的魔力。我們展開(kāi)它(注意,因?yàn)镻wr需要兩個(gè)參數(shù),而我們這里只給出了一個(gè),所以Pwr(f_gen(f_gen))得到的是一個(gè)單參(即n)的函數(shù)):
Pwr(f_gen(f_gen)) => If_Else n==0 1 n*f_gen(f_gen) (n-1)
而里面的那個(gè)f_gen(f_gen),根據(jù)f_gen的定義,又會(huì)展開(kāi)為Pwr(f_gen(f_gen)),所以:
Pwr(f_gen(f_gen)) => If_Else n==0 1 n* Pwr(f_gen(f_gen)) (n-1)
因?yàn)镻wr(f_gen(f_gen))是一個(gè)接受n為參數(shù)的函數(shù),所以不妨把它令成f(f的參數(shù)是n),這樣上面的式子就是:
f => If_Else n==0 1 n*f(n-1)
完美的階乘函數(shù)!
3. 哥德?tīng)柕牟煌陚湫远ɡ?/h2>
漫長(zhǎng)的Y Combinator征途仍然并非本文的最終目的,關(guān)鍵是馬上你會(huì)看到Y(jié) combinator可以由哥德?tīng)柌煌陚湫远ɡ碜C明的一個(gè)核心構(gòu)造式一眼瞧出來(lái)!
讓我們的思緒回到1931年,那個(gè)數(shù)學(xué)界風(fēng)起云涌的年代,一個(gè)名不經(jīng)傳的20出頭的學(xué)生,在他的博士論文中證明了一個(gè)驚天動(dòng)地的結(jié)論。
在那個(gè)年代,希爾伯特的數(shù)學(xué)天才就像太陽(yáng)的光芒一般奪目,在關(guān)于數(shù)學(xué)嚴(yán)格化的大紛爭(zhēng)中希爾伯特帶領(lǐng)的形式主義派系技?jí)喝盒郏玫皆S多當(dāng)時(shí)有名望的數(shù)學(xué)家的支持。希爾伯特希望借助于形式化的手段,抽掉數(shù)學(xué)證明中的意義,把數(shù)學(xué)證明抽象成一堆無(wú)意義的符號(hào)轉(zhuǎn)換,就連我們?nèi)祟?lèi)賴(lài)以自豪的邏輯推導(dǎo),也不過(guò)只是一堆堆符號(hào)轉(zhuǎn)換而已(想起lambda calculus系統(tǒng)了吧:))。這樣一來(lái),一個(gè)我們?nèi)粘K^的,帶有直觀(guān)意義和解釋的數(shù)學(xué)系統(tǒng)就變成了一個(gè)純粹由無(wú)意義符號(hào)表達(dá)的、公理加上推導(dǎo)規(guī)則所構(gòu)成的形式系統(tǒng),而數(shù)學(xué)證明呢,只不過(guò)是在這個(gè)系統(tǒng)內(nèi)玩的一個(gè)文字游戲。令人驚訝的是,這樣一種做法,真的是可行的!數(shù)學(xué)的意義,似乎竟然真的可以被抽掉!另一方面,一個(gè)形式系統(tǒng)具有非常好的性質(zhì),平時(shí)人們證明一個(gè)定理所動(dòng)用的推導(dǎo),變成了純粹機(jī)械的符號(hào)變換。希爾伯特希望能夠證明,在任一個(gè)無(wú)矛盾的形式系統(tǒng)中所能表達(dá)的所有陳述都要么能夠證明要么能夠證偽。這看起來(lái)是個(gè)非常直觀(guān)的結(jié)論,因?yàn)橐粋€(gè)結(jié)論要么是真要么是假,而它在它所處的領(lǐng)域/系統(tǒng)中當(dāng)然應(yīng)該能夠證明或證偽了(只要我們能夠揭示出該系統(tǒng)中足夠多的真理)。
然而,哥德?tīng)柕淖C明無(wú)情的擊碎了這一企圖,哥德?tīng)柕淖C明揭示出,任何足夠強(qiáng)到蘊(yùn)含了皮亞諾算術(shù)系統(tǒng)(PA)的一致(即無(wú)矛盾)的系統(tǒng)都是不完備的,所謂不完備也就是說(shuō)在系統(tǒng)內(nèi)存在一個(gè)為真但無(wú)法在系統(tǒng)內(nèi)推導(dǎo)出的命題。這在當(dāng)時(shí)的數(shù)學(xué)界揭起了軒然大波,其證明不僅具有數(shù)學(xué)意義,而且蘊(yùn)含了深刻的哲學(xué)意義。從那時(shí)起這一不完備性定理就被引申到自然科學(xué)乃至人文科學(xué)的各個(gè)角落…至今還沒(méi)有任何一個(gè)數(shù)學(xué)定理居然能夠產(chǎn)生這么廣泛而深遠(yuǎn)的影響。
哥德?tīng)柕淖C明非常的長(zhǎng),達(dá)到了200多頁(yè)紙,但其中很大的成分是用在了一些輔助性的工作上面,比如占據(jù)超過(guò)1/3紙張的是關(guān)于一個(gè)形式系統(tǒng)如何映射到自然數(shù),也就是說(shuō),如何把一個(gè)形式系統(tǒng)中的所有公式都表示為自然數(shù),并可以從一自然數(shù)反過(guò)來(lái)得出相應(yīng)的公式。這其實(shí)就是編碼,在我們現(xiàn)在看來(lái)是很顯然的,因?yàn)橐粋€(gè)程序就可以被編碼成二進(jìn)制數(shù),反過(guò)來(lái)也可以解碼。但是在當(dāng)時(shí)這是一個(gè)全新的思想,也是最關(guān)鍵的輔助性工作之一,另一方面,這正是“程序即數(shù)據(jù)”的最初想法。
現(xiàn)在我們知道,要證明哥德?tīng)柕牟煌陚湫远ɡ恚恍柙诩俣ǖ男问较到y(tǒng)T內(nèi)表達(dá)出一個(gè)為真但無(wú)法在T內(nèi)推導(dǎo)出(證明)的命題。于是哥德?tīng)枠?gòu)造了這樣一個(gè)命題,用自然語(yǔ)言表達(dá)就是:命題P說(shuō)的是“P不可在系統(tǒng)T內(nèi)證明”(這里的系統(tǒng)T當(dāng)然就是我們的命題P所處的形式系統(tǒng)了),也就是說(shuō)“我不可以被證明”,跟著名的說(shuō)謊者悖論非常相似,只是把“說(shuō)謊”改成了“不可以被證明”。我們注意到,一旦這個(gè)命題能夠在T內(nèi)表達(dá)出來(lái),我們就可以得出“P為真但無(wú)法在T內(nèi)推導(dǎo)出來(lái)”的結(jié)論,從而證明T的不完備性。為什么呢?我們假設(shè)T可以證明出P,而因?yàn)镻說(shuō)的就是P不可在系統(tǒng)T內(nèi)證明,于是我們又得到T無(wú)法證明出P,矛盾產(chǎn)生,說(shuō)明我們的假設(shè)“T可以證明P”是錯(cuò)誤的,根據(jù)排中律,我們得到T不可以證明P,而由于P說(shuō)的正是“T不可證明P”,所以P就成了一個(gè)正確的命題,同時(shí)無(wú)法由T內(nèi)證明!
如果你足夠敏銳,你會(huì)發(fā)現(xiàn)上面這番推理本身不就是證明嗎?其證明的結(jié)果不就是P是正確的?然而實(shí)際上這番證明是位于T系統(tǒng)之外的,它用到了一個(gè)關(guān)于T系統(tǒng)的假設(shè)“T是一致(無(wú)矛盾)的”,這個(gè)假設(shè)并非T系統(tǒng)里面的內(nèi)容,所以我們剛才其實(shí)是在T系統(tǒng)之外推導(dǎo)出了P是正確的,這跟P不能在T之內(nèi)推導(dǎo)出來(lái)并不矛盾。所以別擔(dān)心,一切都正常。
那么,剩下來(lái)最關(guān)鍵的問(wèn)題就是如何用形式語(yǔ)言在T內(nèi)表達(dá)出這個(gè)P,上面的理論雖然漂亮,但若是P根本沒(méi)法在T內(nèi)表達(dá)出來(lái),我們又如何能證明“T內(nèi)存在這個(gè)為真但無(wú)法被證明的P”呢?那一切還不是白搭?
于是,就有了哥德?tīng)栕C明里面最核心的構(gòu)造,哥德?tīng)枠?gòu)造了這樣一個(gè)公式:
N(n) is unprovable in T
這個(gè)公式由兩部分構(gòu)成,n是這個(gè)公式的自由變量,它是一個(gè)自然數(shù),一旦給定,那么這個(gè)公式就變成一個(gè)明確的命題。而N則是從n解碼出的貨真價(jià)實(shí)的(即我們常見(jiàn)的符號(hào)形式的)公式(記得哥德?tīng)柕淖C明第一部分就是把公式編碼嗎?)。”is unprovable in T”則是一個(gè)謂詞,這里我們沒(méi)有用形式語(yǔ)言而是用自然語(yǔ)言表達(dá)出來(lái)的,但哥德?tīng)栕C明了它是可以用形式語(yǔ)言表達(dá)出來(lái)的,大致思路就是:一個(gè)形式系統(tǒng)中的符號(hào)數(shù)目是有限的,它們構(gòu)成這個(gè)形式系統(tǒng)的符號(hào)表。于是,我們可以依次枚舉出所有長(zhǎng)度為1的串,長(zhǎng)度為2的串,長(zhǎng)度為3的串… 此外根據(jù)形式系統(tǒng)給出的語(yǔ)法規(guī)則,我們可以檢查每個(gè)串是否是良構(gòu)的公式(well formed formula,簡(jiǎn)稱(chēng)wff,其實(shí)也就是說(shuō),是否符合語(yǔ)法規(guī)則,前面我們?cè)诮榻Blambda calculus的時(shí)候看到了,一個(gè)形式系統(tǒng)是需要語(yǔ)法規(guī)則的,比如邏輯語(yǔ)言形式化之后我們就會(huì)看到P->Q是一個(gè)wff,而->PQ則不是),因而我們就可以枚舉出所有的wff來(lái)。最關(guān)鍵的是,我們觀(guān)察到形式系統(tǒng)中的證明也不過(guò)就是由一個(gè)個(gè)的wff構(gòu)成的序列(想想推導(dǎo)的過(guò)程,不就是一個(gè)公式接一個(gè)公式嘛)。而wff構(gòu)成的序列本身同樣也是由符號(hào)表內(nèi)的符號(hào)構(gòu)成的串。所以我們只需枚舉所有的串,對(duì)每一個(gè)串檢查它是否是一個(gè)由wff構(gòu)成的序列(證明),如果是,則記錄下這個(gè)wff序列(證明)的最后一個(gè)wff,也就是它的結(jié)論。這樣我們便枚舉出了所有的可由T推導(dǎo)出的定理。然后為了表達(dá)出”X is unprovable in T”,本質(zhì)上我們只需說(shuō)“不存在這樣一個(gè)自然數(shù)S,它所解碼出來(lái)的wff序列以X為終結(jié)”!這也就是說(shuō),我們表達(dá)出了“is unprovable in T”這個(gè)謂詞。
我們用UnPr(X)來(lái)表達(dá)“X is unprovable in T”,于是哥德?tīng)柕墓阶兂闪耍?/p>
UnPr( N(n) )
現(xiàn)在,到了最關(guān)鍵的部分,首先我們把這個(gè)公式簡(jiǎn)記為G(n)——?jiǎng)e忘了G內(nèi)有一個(gè)自由變量n,所以G現(xiàn)在還不是一個(gè)命題,而只是一個(gè)公式,所以談不上真假:
G(n): UnPr( N(n) )
又由于G也是個(gè)wff,所以它也有自己的編碼g,當(dāng)然g是一個(gè)自然數(shù),現(xiàn)在我們把g作為G的參數(shù),也就是說(shuō),把G里面的自由變量n替換為g,我們于是得到一個(gè)真正的命題:
G(g): UnPr( G(g) )
用自然語(yǔ)言來(lái)說(shuō),這個(gè)命題G(g)說(shuō)的就是“我是不可在T內(nèi)證明的”。看,我們?cè)谛问较到y(tǒng)T內(nèi)表達(dá)出了“我是不可在T內(nèi)證明的”這個(gè)命題。而我們一開(kāi)始已經(jīng)講過(guò)了如何用這個(gè)命題來(lái)推斷出G(g)為真但無(wú)法在T內(nèi)證明,于是這就證明了哥德?tīng)柕牟煌陚湫远ɡ怼?/p>
哥德?tīng)柕牟煌陚湫远ɡ肀环Q(chēng)為20世紀(jì)數(shù)學(xué)最重大的發(fā)現(xiàn)(不知道有沒(méi)有“之一”:) )現(xiàn)在我們知道為真但無(wú)法在系統(tǒng)內(nèi)證明的命題不僅僅是這個(gè)詭異的“哥德?tīng)柮}”,還有很多真正有意義的明確命題,其中最著名的就是連續(xù)統(tǒng)假設(shè),此外哥德巴赫猜想也有可能是個(gè)沒(méi)法在數(shù)論系統(tǒng)中證明的真命題。
哥德?tīng)柕牟煌陚湫远ɡ碜C明了數(shù)學(xué)是一個(gè)未完結(jié)的學(xué)科,永遠(yuǎn)有需要我們以人的頭腦從系統(tǒng)之外去用我們獨(dú)有的直覺(jué)發(fā)現(xiàn)的東西。羅杰·彭羅斯在《The Emperor’s New Mind》中用它來(lái)證明人工智能的不可實(shí)現(xiàn)。當(dāng)然,這個(gè)結(jié)論是很受質(zhì)疑的。但哥德?tīng)柕牟煌陚湫远ɡ淼拇_還有很多很多的有趣推論,數(shù)學(xué)的和哲學(xué)上的。哥德?tīng)柕牟煌陚湫远ɡ碜钌羁痰牡胤骄褪撬沂玖俗灾福ɑ蚍Q(chēng)自引用,遞歸調(diào)用自身等等)結(jié)構(gòu)的普遍存在性,我們?cè)賮?lái)看一看哥德?tīng)柮}的絕妙構(gòu)造:
G(n): UnPr( N(n) )
我們注意到,這里的UnPr其實(shí)是一個(gè)形式化的謂詞,它不一定要說(shuō)“X在T內(nèi)可證明”,我們可以把它泛化為一個(gè)一般化的謂詞,P:
G(n): P( N(n) )
也就是說(shuō),對(duì)于任意一個(gè)單參的謂詞P,都存在上面這個(gè)哥德?tīng)柟健H缓笪覀兯愠鲞@個(gè)哥德?tīng)柟降淖匀粩?shù)編碼g,然后把它扔給G,就得到:
G(g): P( G(g) )
是不是很熟悉這個(gè)結(jié)構(gòu)?我們的Y Combinator的構(gòu)造不就是這樣一個(gè)形式?我們把G和P都看成一元函數(shù),G(g)可不正是P這個(gè)函數(shù)的不動(dòng)點(diǎn)么!于是,我們從哥德?tīng)柕淖C明里面直接看到了Y Combinator!
哥德?tīng)柕淖C明雖然巧妙至極,然而其背后的思維過(guò)程仍然飄逸而不可捉摸,至少我當(dāng)時(shí)看到G(n)的時(shí)候,“乃大驚”“不知所從出”,他怎么想到的?難道是某一個(gè)瞬間“靈光一現(xiàn)”?一般我是不信這一說(shuō)的,已經(jīng)有越來(lái)越多的科學(xué)研究表明一瞬間的“靈感”往往是潛意識(shí)乃至表層意識(shí)長(zhǎng)期思考的結(jié)果。哥德?tīng)柼觳诺淖C明也不例外,我們馬上就會(huì)看到,在這個(gè)神秘的構(gòu)造背后,其實(shí)隱藏著某種更深的東西,這就是康托爾在19世紀(jì)80年代研究無(wú)窮集合和超限數(shù)時(shí)引入的對(duì)角線(xiàn)方法。這個(gè)方法仿佛有種神奇的力量,能夠揭示出某種自指的結(jié)構(gòu)來(lái),而同時(shí),這又是一個(gè)極度簡(jiǎn)單的手法,通過(guò)它我們能夠得到數(shù)學(xué)里面一些非常奇妙的性質(zhì)。無(wú)論是哥德?tīng)柕牟煌陚湫远ɡ磉€是再后來(lái)丘齊建立的lambda calculus,抑或我們非常熟悉的圖靈機(jī)理論里的停機(jī)問(wèn)題,其實(shí)都只是這個(gè)手法簡(jiǎn)單推演的結(jié)果!
4.大道至簡(jiǎn)——康托爾的天才
康托爾在無(wú)窮集合和超限數(shù)方面的工作主要集中在兩篇突破性的論文上。不過(guò)這里就不過(guò)多談?wù)摂?shù)學(xué)的細(xì)節(jié)了,只說(shuō)康托爾引入對(duì)角線(xiàn)方法的動(dòng)機(jī)和什么是對(duì)角線(xiàn)方法。
康托爾在研究無(wú)窮集合的時(shí)候,富有洞察性地看到了對(duì)于無(wú)窮集合的大小問(wèn)題,我們不能再使用直觀(guān)的“所含元素的個(gè)數(shù)”來(lái)描述,于是他創(chuàng)造性地將一一對(duì)應(yīng)引入進(jìn)來(lái),兩個(gè)無(wú)窮集合“大小”一樣當(dāng)且僅當(dāng)它們的元素之間能夠構(gòu)成一一對(duì)應(yīng)。這是一個(gè)非常直觀(guān)的概念,一一對(duì)應(yīng)嘛,當(dāng)然個(gè)數(shù)相等了,是不是呢?然而這同時(shí)就是它不直觀(guān)的地方了。對(duì)于無(wú)窮集合,我們?nèi)粘5乃^“個(gè)數(shù)”的概念不管用了,因?yàn)闊o(wú)窮集合里面的元素個(gè)數(shù)本就是無(wú)窮多個(gè)。不信我們來(lái)看一個(gè)小小的例子。我們說(shuō)自然數(shù)集合能夠跟偶數(shù)集合構(gòu)成一一對(duì)應(yīng),從而自然數(shù)集合跟偶數(shù)集合里面元素“個(gè)數(shù)”是一樣多的。怎么可能?偶數(shù)集合是自然數(shù)集合的真子集,所有偶數(shù)都是自然數(shù),但自然數(shù)里面還包含奇數(shù)呢,說(shuō)起來(lái)應(yīng)該是二倍的關(guān)系不是?不是!我們只要這樣來(lái)構(gòu)造一一對(duì)應(yīng):
1 2 3 4 …
2 4 6 8 …
用函數(shù)來(lái)描述就是 f(n) = 2n。檢驗(yàn)一下是不是一一對(duì)應(yīng)的?不可思議對(duì)嗎?還有更不可思議的,自然數(shù)集是跟有理數(shù)集一一對(duì)應(yīng)的!對(duì)應(yīng)函數(shù)的構(gòu)造就留給你解決吧,提示,按如下方式來(lái)挨個(gè)數(shù)所有的有理數(shù):
1/1 1/2 2/1 1/3 2/2 3/1 1/4 2/3 3/2 4/1 …
用這種一一對(duì)應(yīng)的手法還可以得到很多驚人的結(jié)論,如一條直線(xiàn)上所有的點(diǎn)跟一個(gè)平面上所有的點(diǎn)構(gòu)成一一對(duì)應(yīng)(也就是說(shuō)復(fù)數(shù)集合跟實(shí)數(shù)集合構(gòu)成一一對(duì)應(yīng))。以致于連康托爾自己都不敢相信自己的眼睛了,這也就是為什么他在給戴得金的信中會(huì)說(shuō)“我看到了它,卻不敢相信它”的原因。
然而,除了一一對(duì)應(yīng)之外,還有沒(méi)有不能構(gòu)成一一對(duì)應(yīng)的兩個(gè)無(wú)窮集合呢?有。實(shí)數(shù)集合就比自然數(shù)集合要“大”,它們之間實(shí)際上無(wú)法構(gòu)成一一對(duì)應(yīng)。這就是康托爾的對(duì)角線(xiàn)方法要解決的問(wèn)題。
實(shí)數(shù)集和自然數(shù)集無(wú)法構(gòu)成一一對(duì)應(yīng)?!
我們只需將實(shí)數(shù)的小數(shù)位展開(kāi),并且我們假設(shè)實(shí)數(shù)集能夠與自然數(shù)集一一對(duì)應(yīng),也就是說(shuō)假設(shè)實(shí)數(shù)集可列,所以我們把它們與自然數(shù)一一對(duì)應(yīng)列出,如下:
1 a10.a11a12a13…
2 a20.a21a22a23…
3 a30.a31a32a33…
4 …
5 …
(注:aij里面的ij是下標(biāo))
現(xiàn)在,我們構(gòu)造一個(gè)新的實(shí)數(shù),它的第i位小數(shù)不等于aii。也就是說(shuō),它跟上面列出的每一個(gè)實(shí)數(shù)都至少有一個(gè)對(duì)應(yīng)的小數(shù)位不等,也就是說(shuō)它不等于我們上面列出的所有實(shí)數(shù),這跟我們上面假設(shè)已經(jīng)列出了所有實(shí)數(shù)的說(shuō)法相矛盾。所以實(shí)數(shù)集只能是不可列的,即不可與自然數(shù)集一一對(duì)應(yīng)!這是對(duì)角線(xiàn)方法的最簡(jiǎn)單應(yīng)用。
對(duì)角線(xiàn)方法有很多非常奇妙的結(jié)論,其中之一就是文章一開(kāi)始提到的停機(jī)問(wèn)題。我想絕大多數(shù)人剛接觸停機(jī)問(wèn)題的時(shí)候都有一個(gè)問(wèn)題,圖靈怎么能夠想到這么詭異的證明,怎么能構(gòu)造出那個(gè)詭異的“說(shuō)停機(jī)又不停機(jī),說(shuō)不停機(jī)又停機(jī)”的悖論機(jī)器。馬上我們就會(huì)看到,這其實(shí)只是對(duì)角線(xiàn)方法的一個(gè)直接結(jié)論。
還是從反證開(kāi)始,我們假設(shè)存在這樣一個(gè)圖靈機(jī),他能夠判斷任何程序在任何輸入上是否停機(jī)。由于所有圖靈機(jī)構(gòu)成的集合是一個(gè)可列集(也就是說(shuō),我們可以逐一列出所有的圖靈機(jī)),所以我們可以很自然地列出下表,它表示每個(gè)圖靈機(jī)分別在每一個(gè)可能的輸入(1,2,3,…)下的輸出,N表示無(wú)法停機(jī),其余數(shù)值則表示停機(jī)后的輸出:
1 2 3 4 …
M1 N 1 N N …
M2 2 0 N 0 …
M3 0 1 2 0 …
M4 N 0 5 N …
…
M1,M2,M3 … 是逐一列出的圖靈機(jī),并且,注意,由于程序即數(shù)據(jù),每個(gè)圖靈機(jī)都有唯一編碼,所以我們規(guī)定在枚舉圖靈機(jī)的時(shí)候Mi其實(shí)就代表編碼為i的圖靈機(jī),當(dāng)然這里很多圖靈機(jī)將會(huì)是根本沒(méi)用的玩意,但這不要緊。此外,最上面的一行1 2 3 4 … 是輸入數(shù)據(jù),如,矩陣的第一行代表M1分別在1,2,3,…上面的輸出,不停機(jī)的話(huà)就是N。
我們剛才假設(shè)存在這樣一個(gè)圖靈機(jī)H,它能夠判斷任何程序在任何輸入上能否停機(jī),換句話(huà)說(shuō),H(i,j)(i是Mi的編碼)能夠給出“Mi(j)”是N(不停)呢還是給出一個(gè)具體的結(jié)果(停)。
我們現(xiàn)在來(lái)運(yùn)用康托爾的對(duì)角線(xiàn)方法,我們構(gòu)造一個(gè)新的圖靈機(jī)P,P在1上的輸出行為跟M1(1)“不一樣”,在2上的輸出行為跟M2(2)“不一樣”,…總之P在輸入i上的輸出跟Mi(i)不一樣。只需利用一下我們?nèi)f能的H,這個(gè)圖靈機(jī)P就不難構(gòu)造出來(lái),如下:
P(i):
if( H(i, i) == 1 ) then // Mi(i) halts
return 1 + Mi(i)
else // if H(i, i) == 0 (Mi(i) doesn’t halt)
return 0
也就是說(shuō),如果Mi(i)停機(jī),那么P(i)的輸出就是Mi(i)+1,如果Mi(i)不停機(jī)的話(huà),P(i)就停機(jī)且輸出0。這就保證了P(i)的輸出行為跟Mi(i)反正不一樣。現(xiàn)在,我們注意到P本身是一個(gè)圖靈機(jī),而我們上面已經(jīng)列出了所有的圖靈機(jī),所以必然存在一個(gè)k,使得Mk = P。而兩個(gè)圖靈機(jī)相等當(dāng)且僅當(dāng)它們對(duì)于所有的輸入都相等,也就是說(shuō)對(duì)于任取的n,有Mk(n) = P(n),現(xiàn)在令n=k,得到Mk(k)=P(k),根據(jù)上面給出的P的定義,這實(shí)際上就是:
Mk(k) = P(k) =
1+Mk(k) if Mk(k) halts
0 if Mk(k) doesn’t halt
看到這個(gè)式子里蘊(yùn)含的矛盾了嗎?如果Mk(k)停機(jī),那么Mk(k)=1+Mk(k);如果Mk(k)不停機(jī),則Mk(k)=0(給出結(jié)果0即意味著Mk(k)停機(jī));不管哪種情況都是矛盾。于是我們得出,不存在那樣的H。
這個(gè)對(duì)角線(xiàn)方法實(shí)際上說(shuō)明了,無(wú)論多聰明的H,總存在一個(gè)圖靈機(jī)的停機(jī)行為是它無(wú)法判斷的。這跟哥德?tīng)柖ɡ怼盁o(wú)論多‘完備’的形式化公理系統(tǒng),都存在一個(gè)‘哥德?tīng)柮}’是無(wú)法在系統(tǒng)內(nèi)推導(dǎo)出來(lái)的”從本質(zhì)上其實(shí)是一模一樣的。只不過(guò)我們一般把圖靈的停機(jī)問(wèn)題稱(chēng)為“可判定問(wèn)題”,而把數(shù)學(xué)的稱(chēng)為“可證明問(wèn)題”。
等等!如果我們把那個(gè)無(wú)法判定是否停機(jī)的圖靈機(jī)作為算法的特例納入到我們的H當(dāng)中呢?我們把得到的新的判定算法記為H1。然而,可惜的是,在H1下,我們又可以相應(yīng)地以同樣的手法從H1構(gòu)造出一個(gè)無(wú)法被它(H1)判定的圖靈機(jī)來(lái)。你再加,我再構(gòu)造,無(wú)論你加多少個(gè)特例進(jìn)去,我都可以由同樣的方式構(gòu)造出來(lái)一個(gè)你無(wú)法夠到的圖靈機(jī),以彼之矛,攻彼之盾。其實(shí)這也是哥德?tīng)柖ɡ碜钌羁痰慕Y(jié)論之一,哥德?tīng)柖ɡ砥鋵?shí)就說(shuō)明了無(wú)論你給出多少個(gè)公理,即無(wú)論你建立多么完備的公理體系,這個(gè)系統(tǒng)里面都有由你的那些公理出發(fā)所推導(dǎo)不到的地方,這些黑暗的角落,就是人類(lèi)直覺(jué)之光才能照射到的地方!
本節(jié)我們從對(duì)角線(xiàn)方法證明了圖靈的停機(jī)問(wèn)題,我們看到,對(duì)角線(xiàn)方法能夠揭示出某種自指結(jié)構(gòu),從而構(gòu)造出一個(gè)“悖論圖靈機(jī)”。實(shí)際上,對(duì)角線(xiàn)方法是一種有深遠(yuǎn)影響的方法,哥德?tīng)柕淖C明其實(shí)也是這個(gè)方法的一則應(yīng)用。證明與上面的停機(jī)問(wèn)題證明如出一轍,只不過(guò)把Mi換成了一個(gè)形式系統(tǒng)內(nèi)的公式fi。
羅素悖論
學(xué)過(guò)邏輯的人大約肯定是知道著名的羅素悖論的,羅素悖論用數(shù)學(xué)的形式來(lái)描述就是:
R = {X:X不屬于X};
這個(gè)悖論最初是從康托爾的無(wú)窮集合論里面引申出來(lái)的。當(dāng)初康托爾在思考無(wú)窮集合的時(shí)候發(fā)現(xiàn)可以稱(chēng)“一切集合的集合”,這樣一個(gè)集合由于它本身也是一個(gè)集合,所以它就屬于它自身。也就是說(shuō),我們現(xiàn)在可以稱(chēng)世界上存在一類(lèi)屬于自己的集合,除此之外當(dāng)然就是不屬于自己的集合了。而我們把所有不屬于自己的集合收集起來(lái)做成一個(gè)集合R,這就是著名的羅素悖論了。
我們來(lái)看R是否屬于R,如果R屬于R,根據(jù)R的定義,R就不應(yīng)該屬于R。而如果R不屬于R,則再次根據(jù)R的定義,R就應(yīng)該屬于R。
這個(gè)悖論促使了集合論的公理化。后來(lái)策梅羅公理化的集合論里面就不允許X屬于X(不過(guò)可惜的是,盡管如此還是沒(méi)法證明這樣的集合論不可能產(chǎn)生出新的悖論。而且永遠(yuǎn)沒(méi)法證明——這就是哥德?tīng)柕诙煌陚湫远ɡ淼慕Y(jié)論——一個(gè)包含了PA的形式化公理系統(tǒng)永遠(yuǎn)無(wú)法在內(nèi)部證明其自身的一致(無(wú)矛盾)性。從而希爾伯特想從元數(shù)學(xué)推出所有數(shù)學(xué)系統(tǒng)的一致性的企圖也就失敗了,因?yàn)樵獢?shù)學(xué)的一致性又得由元元數(shù)學(xué)來(lái)證明,后者的一致性又得由元元元數(shù)學(xué)來(lái)證明…)。
這里我們只關(guān)心羅素是如何想出這個(gè)絕妙的悖論的。還是對(duì)角線(xiàn)方法!我們羅列出所有的集合,S1,S2,S3 …
S1 S2 S3 …
S1 0 1 1 …
S2 1 1 0 …
S3 0 0 0 …
… …
右側(cè)縱向列出所有集合,頂行橫向列出所有集合。0/1矩陣的(i,j)處的元素表示Si是否包含Sj,記為Si(j)。現(xiàn)在我們只需構(gòu)造一個(gè)新的0/1序列L,它的第i位與矩陣的(i,i)處的值恰恰相反:L(i) = 1-Si(i)。我們看到,這個(gè)新的序列其實(shí)對(duì)應(yīng)了一個(gè)集合,不妨也記為L(zhǎng),L(i)表示L是否包含Si。根據(jù)L的定義,如果矩陣的(i,i)處值為0(也就是說(shuō),如果Si不包含Si),那么L這個(gè)集合就包含Si,否則就不包含。我們注意到這個(gè)新的集合L肯定等于某個(gè)Sk(因?yàn)槲覀円呀?jīng)列出了所有的集合),L = Sk。既然L與Sk是同一集合,那么它們肯定包含同樣的元素,從而對(duì)于任意n,有L(n) = Sk(n)。于是通過(guò)令n=k,得到L(k) = Sk(k),而根據(jù)L的定義,L(k) = 1- Sk(k)。這就有Sk(k) = 1-Sk(k),矛盾。
通過(guò)抽象簡(jiǎn)化以上過(guò)程,我們看到,我們構(gòu)造的L其實(shí)是“包含了所有不包含它自身的集合的集合”,用數(shù)學(xué)的描述正是羅素悖論!
敏銳的你可能會(huì)注意到所有集合的數(shù)目是不可數(shù)的從而根本不能S1,S2…的一一列舉出來(lái)。沒(méi)錯(cuò),但通過(guò)假設(shè)它們可以列舉出來(lái),我們發(fā)現(xiàn)了一個(gè)與可列性無(wú)關(guān)的悖論。所以這里的對(duì)角線(xiàn)方法其實(shí)可以說(shuō)是一種啟發(fā)式方法。
希爾伯特第十問(wèn)題結(jié)出的碩果
希爾伯特是在1900年巴黎數(shù)學(xué)家大會(huì)上提出著名的希爾伯特第十問(wèn)題的,簡(jiǎn)言之就是是否存在一個(gè)算法,能夠計(jì)算任意丟番圖方程是否有整根。要解決這個(gè)問(wèn)題,就得先嚴(yán)格定義“算法”這一概念。為此圖靈和丘齊分別提出了圖靈機(jī)和lambda calculus這兩個(gè)概念,它們從不同的角度抽象出了“有效(機(jī)械)計(jì)算”的概念,著名的圖靈——丘齊命題就是說(shuō)所有可以有效計(jì)算出來(lái)的問(wèn)題都可以由圖靈機(jī)計(jì)算出來(lái)。實(shí)際上我們已經(jīng)看到,丘齊的lambda calculus其實(shí)就是數(shù)學(xué)推理系統(tǒng)的一個(gè)形式化。而圖靈機(jī)則是把這個(gè)數(shù)學(xué)概念物理化了。而也正因?yàn)閳D靈機(jī)的概念隱含了實(shí)際的物理實(shí)現(xiàn),所以馮·諾依曼才據(jù)此提出了奠定現(xiàn)代計(jì)算機(jī)體系結(jié)構(gòu)的馮·諾依曼體系結(jié)構(gòu),其遵循的,正是圖靈機(jī)的概念。而“程序即數(shù)據(jù)”的理念,這個(gè)發(fā)端于數(shù)學(xué)家哥德?tīng)柕牟煌陚湫远ɡ淼淖C明之中的理念,則早就在黑暗中預(yù)示了可編程機(jī)器的必然問(wèn)世。
5. 總結(jié)
哥德?tīng)柕牟煌陚湫远ɡ碚鸷沉?0世紀(jì)數(shù)學(xué)界的天空,其數(shù)學(xué)意義顛覆了希爾伯特的形式化數(shù)學(xué)的宏偉計(jì)劃,其哲學(xué)意義直到21世紀(jì)的今天仍然不斷被延伸到各個(gè)自然學(xué)科,深刻影響著人們的思維。圖靈為了解決希爾伯特著名的第十問(wèn)題而提出有效計(jì)算模型,進(jìn)而作出了可計(jì)算理論和現(xiàn)代計(jì)算機(jī)的奠基性工作,著名的停機(jī)問(wèn)題給出了機(jī)械計(jì)算模型的能力極限,其深刻的意義和漂亮的證明使它成為可計(jì)算理論中的標(biāo)志性定理之一。丘齊,跟圖靈同時(shí)代的天才,則從另一個(gè)抽象角度提出了lambda算子的思想,與圖靈機(jī)抽象的傾向于硬件性不同,丘齊的lambda算子理論是從數(shù)學(xué)的角度進(jìn)行抽象,不關(guān)心運(yùn)算的機(jī)械過(guò)程而只關(guān)心運(yùn)算的抽象性質(zhì),只用最簡(jiǎn)潔的幾條公理便建立起了與圖靈機(jī)完全等價(jià)的計(jì)算模型,其體現(xiàn)出來(lái)的數(shù)學(xué)抽象美開(kāi)出了函數(shù)式編程語(yǔ)言這朵奇葩。而誕生于函數(shù)式編程語(yǔ)言的神奇的Y combinator至今仍然讓人們陷入深沉的震撼和反思當(dāng)中… 然而,這一切的一切,看似不很相關(guān)卻又有點(diǎn)相關(guān),認(rèn)真思考其關(guān)系卻又有點(diǎn)一頭霧水的背后,其實(shí)隱隱藏著一條線(xiàn),這條線(xiàn)把它們從本質(zhì)上串到了一起。這條線(xiàn)的盡頭,不是別人,正是只手撥開(kāi)被不嚴(yán)密性問(wèn)題困擾的19世紀(jì)數(shù)學(xué)界陰沉天空的天才數(shù)學(xué)家康托爾,康托爾創(chuàng)造性地將一一對(duì)應(yīng)和對(duì)角線(xiàn)方法運(yùn)用到無(wú)窮集合理論的建立當(dāng)中,這個(gè)被希爾伯特稱(chēng)為“誰(shuí)也無(wú)法將我們從康托爾為我們創(chuàng)造的樂(lè)園中驅(qū)逐出去”、被羅素稱(chēng)為“19世紀(jì)最偉大的智者之一”的人,他在集合論方面的工作終于驅(qū)散了不嚴(yán)密性問(wèn)題帶來(lái)的陰霾,仿佛一道金色的陽(yáng)光刺破烏云,19世紀(jì)的數(shù)學(xué)終于看到了真正嚴(yán)格化的曙光,數(shù)學(xué)終于得以站在了前所未有的堅(jiān)固的基礎(chǔ)之上;集合論至今仍是數(shù)學(xué)里最基礎(chǔ)和最重要的理論之一。而康托爾當(dāng)初在研究無(wú)窮集合時(shí)最具天才的方法之一——對(duì)角線(xiàn)方法——?jiǎng)t帶來(lái)了極其深遠(yuǎn)的影響,其純粹而直指事物本質(zhì)的思想如洪鐘大呂般響徹?cái)?shù)學(xué)和哲學(xué)的每一個(gè)角落。
在這篇讀書(shū)筆記中,我們依次介紹了停機(jī)問(wèn)題、Y combinator、哥德?tīng)柌煌陚湫远ɡ怼?duì)角線(xiàn)方法。從康托爾的對(duì)角線(xiàn)方法出發(fā),我們可以輕而易舉地推導(dǎo)出哥德?tīng)柕牟煌陚湫远ɡ恚珊笳哂挚梢暂p易導(dǎo)出停機(jī)問(wèn)題和Y combinator,實(shí)際上,后兩者也可以直接由康托爾的對(duì)角線(xiàn)方法導(dǎo)出。尤其是Y combinator,這個(gè)看上去神秘莫測(cè)的算子,如果從哥德?tīng)柕牟煌陚湫远ɡ沓霭l(fā),它甚至比停機(jī)問(wèn)題還要來(lái)得直接簡(jiǎn)單。
啊!數(shù)學(xué)多么美妙啊!