懶漢式遞歸——瞬間明白漢諾塔問題
Q. 為什么會有遞歸?
A. 因為我們是人,不是電腦!我們的working
memory有限!
游戲規則:
有A,B,C三根針,將A針上N個從小到大疊放的盤子移動到C針,一次只能移動一個,不重復移動,小盤子必須在大盤子上面。
問題:
總的移動次數是多少?
分析:
首先明確,我們的目標是將A針上所有N個盤子移動至C針。而對於B針,我們可以將之看成一個中轉站。
這個問題,順向思維或者逆向思維道理是相同的,都太麻煩。我們不妨從中間開始思考。
||: 規則要求小盤子必須在大盤子之上。試想這個過程中,必然會經歷那么一個步驟,即有一大坨N-1個盤子在B針這個中轉站,而我們正將最大那個盤子(即第N個盤子)從A針移動至C針。

【圖例】
只有經歷“移動最大盤子”這個步驟,余下的事情才有可能實現。而在此之前,我們所要做的事情,就是讓“移動最大盤子”這個步驟得以實現。
現在,游戲整個過程以“移動最大盤子”為中央,被分為了兩部分。即(前)“將那坨N-1個盤子從A針移動到B針”,(中)“移動最大盤子”,(后)“將坨N-1個盤子從B針移動到C針”。
這是我們意識到,(前)與(后)操作道理是相似的。不去管那個最大盤子,(前)是以C針為中轉站,(后)是以A針為中轉站。因此兩者所需的移動次數應當是相等的。這意味着我們只要計算出其中一者的移動次數,然而乘以2,在加上“移動最大盤子”的那1次,就是這場游戲的總移動次數了。
用數學語言表達,假設(前)“將N-1個盤子從A針移動到B針”所需次數為Hn-1,總移動次數為Hn,那么可以得出的關系就是:Hn=Hn-1
x 2 + 1.
其實當我們得出這個算式的時候,稍微聰明一點的人已經明白,這就是一個遞推公式,可以直接用此公式得出Hn的通解。
但是LZ比較笨,就是不明白,為什么這個公式就可以套用呢?
那么就干脆繼續思考吧。
讓我們再想象一個情景:最大那個盤子在剛剛從A針被移動到C針,而那坨N-1個盤子還在B針蠢蠢欲動地等待着,即處於(中)->(后)的這個狀態。
怎么移動這N-1個盤子呢?
其實這時候,問題已經回到了筆者標示“||:”符號的地方。“||:”是樂譜中的反復記號,而我們要做的,就是重復上面的步驟,但是要將N替換為N-1,因為現在只剩下N-1個盤子需要移動。而中轉站則從B變成了A(鑒於這時盤子都在B針)。目標仍然是C針。下一次重復的時候,只剩下N-2個盤子需要移動,中轉站又回到B,目標不變仍然是C針。……整個過程中,變化的只是中轉站(在A與B之間輪換),以及剩下那些所需要移動的盤子的總數(越來越少)而已。
那么那個大盤子怎么辦?不去管它嗎??
正解!!
因為你已經把它移到C針,已經完成了這個移動步驟,它不會影響之后的操作。提醒自己牢記游戲規則,大盤子永遠在小盤子下面,而你也不需要再重復移動它——“不重復移動”,正是游戲規則的要求!
於是
Hn=Hn-1 x 2 + 1 這個公式,就可以套用、套用、套用……直到H3=7,H2=3,H1=1。
最后,用最懶的數學歸納法證明通項公式
Hn = 2^n - 1 吧!沒辦法,LZ就是比較懶嘛~
不管這個傳說的可信度有多大,如果考慮一下把64片金片,由一根針上移到另一根針上,並且始終保持上小下大的順序。這需要多少次移動呢?這里需要遞歸的方法。假設有n片,移動次數是f(n).顯然f(1)=1,f(2)=3,f(3)=7,且f(k+1)=2*f(k)+1。此后不難證明f(n)=2^n-1。n=64時,
宇宙壽命
印度傳說
'''
source 原來的盤子
targer 移到目標柱子的盤子
helper 中間柱子的盤子
'''
def hanoi22(n,source,target,helper):
pass
def hanoi(n,A,B,C):
if n == 1:
print(A+'->'+B)
else:
hanoi(n-1,A,C,B)
print(A+'->'+B)
hanoi(n-1,C,B,A)
hanoi(3,'A','B','C')
