帶你理解 Hanoi 漢諾塔遞迴演算法

2021-08-20 22:13:42 字數 3355 閱讀 9815

一. 由遊戲引發的 hanoi 問題

漢諾塔是根據乙個傳說形成的乙個問題。漢諾塔(又稱河內塔)問題是源於印度乙個古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞著 64 片**圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動乙個圓盤。

二. 一種數學問題

我們將 hanoi 問題抽象為一種數學問題。首先給出如下三個柱子 a、b、c,其中 a 柱子上有從上到下從小疊到大的 n 個雲盤。現要求將a柱子上的圓盤都移動到 c 柱子上,其中,每次移動都必須滿足:

每次只能移動乙個圓盤

小圓盤上不能放大圓盤

移動 n 個圓盤最少需要多少次

第 m 步移動的是哪個圓盤以及圓盤移動方向

解題:設總共有 n 個圓盤,steps表示總移動次數

1.對於問題1

(1)if n == 1

第1次 1號盤 a—->c 

steps =1 次

(2)if n == 2

第1次 1號盤 a—->b

第2次 2號盤 a—->c

第3次 1號盤 b—->c

steps = 3 次

(3)if n == 3

第1次 1號盤 a—->c

​第2次 2號盤 a—->b

​第3次 1號盤 c—->b

​第4次 3號盤 a—->c

​第5次 1號盤 b—->a

​第6次 2號盤 b—->c

​第7次 1號盤 a—->c

steps = 7 次

依次往下推,可以發現這樣圓盤個數 n 與移動總次數有這樣乙個規律

1個圓盤的次數 2的1次方減1

2個圓盤的次數 2的2次方減1

3個圓盤的次數 2的3次方減1

​...

......

​n個圓盤的次數 2的n次方減1

所以能得出乙個等式關係 :st

eps=

2n−1

s te

ps=2

n−12.對於問題2

我們使用數學歸納法來理解 hanoi 遞迴演算法。對於遞迴其實就是方法內部呼叫自己,同時也一定有乙個結束點。學習過組合語言的夥伴都應該知道,每次的遞迴呼叫實際上行的是一種棧操作,這個遞迴呼叫其實是方法呼叫棧的過程。每次呼叫時從主線程開始呼叫方法並進行不斷地壓棧和出棧操作。其中,方法的調入就是將方法壓人棧中,方法的結束就是方法出棧的過程。這樣可以保證方法呼叫的順序流,即當函式出現多層巢狀時,需要從外到內一層層把函式壓入棧中,最後棧頂的函式先執行結束(最內層的函式先執行結束)後出棧,再倒數第二層的函式執行結束出棧,到最後,第乙個進棧的函式呼叫結束後從棧中彈出回到主線程,並且結束。

棧的特點是:fifo(先進後出)。比如乙個方法自己呼叫自己,其中乙個呼叫過程也就是進棧過程:a->a(1)->a(2)->a(3)

在a(3)時滿足某種條件得以退出, 回到 a(2), a(2)結束回到a(1), 再回到a, 出棧過程:a(3)->a(2)->a(1)->a

那麼,我們現在來看 hanoi 的遞迴問題,首先來看當前 hanoi 問題的遞迴演算法的實現

### move_hanoi.py

defmove

(num,go,to):

global i

i=i+1

print('第{}步--移動 {} 號圓盤:'.format(i,num), 'move',go,'to',to)

defhanoi

(num,a,b,c):

if num == 1:

move(num,a,c)

return

hanoi(num-1,a,c,b)

move(num,a,c)

hanoi(num-1,b,a,c)

hanoi_move(5,'a','b','c')

i=0

輸出效果如下

看起來是不是很簡單?當時一直都不能懂為什麼就是這樣的遞迴,實際上再聽老師多次提起後,感覺像是懵懵懂懂假裝懂了,具體懂了沒懂,還需要試試**一番。通過證明可能是很好的辦法。

為了證明這中遞迴演算法的合理性,可以採用數學中歸納法來理解。對於遞迴演算法問題也不要妄想一開始就追蹤整個遞迴的全程呼叫。在知乎上有很多非常形象的描述。實際上,如果是對於少於三個盤的 hanoi 移動來說,其實很好理解。但當數目越來越多時,把這個問題展開來想就變得複雜了,這也是為什麼我們需要從層與層之間的交接來理解,而不是展開整個遞迴。

現在討論,當只需要移動乙個圓盤時,此時 n = 1

此時只需移動1次,即將第1個圓盤從a移動到c

步驟  圓盤  柱子移動方向

11 a -> c

當移動兩個盤子,此時 n = 2

步驟  圓盤  柱子移動方向11

a -> b22

a -> c31

b -> c

當移動三個圓盤時,此時n = 3

步驟  圓盤  柱子移動方向11

a -> c22

a -> b31

c -> b43

a -> c51

b -> a62

b -> c71

a -> c

根據上面的移動情況,我們總結出圓盤移動的規律:

再觀察第3個案例中的第 1-3 步 和 第 5-7步

我們用偽**表示以上規律

function move(n,a,b,c)

if n ==1 then

print a;"to",c

else

call move(n-1,a,c,b);//move(n,a,b,c)中 b、c 互換,n=n-1

print a;"to",c

call move(n-1,b,a,c);//move(n,a,b,c)中 b、a 互換,n=n-1

end if

這就是對 hanoi 遞迴演算法的歸納過程

資料引用:

漢若塔互動百科

函式呼叫棧 剖析+**

深刻的理解遞迴漢諾塔(Hanoi)

演算法 1,從目標出發,我們將n個盤子從a移動到b通過c,假如我們這樣寫 hanoi n,a,b,c 2,同理,我們也就可以知道,將n 1個盤子從a移動到b通過c,即就是 hanoi n 1,a,b,c 3,但是我們知道我們一次只能移動乙個盤子,所以,如果我們要將n個盤子從a移動到b通過c,我們可以...

漢諾塔問題 hanoi(遞迴)

漢諾塔問題 hanoi 現有abc三個柱子,a中有n個盤 上小下大 移動到c上,要求全程上小下大 演算法 遞迴,把1 n 1個盤當作乙個整體 include 函式宣告 函式功能 把n個盤 從a 借助b,移到c hanoi int n,char x,char y,char z 函式功能 把乙個盤子x ...

漢諾塔問題(Hanoi塔)

1.將from柱最上面的movesum 1個圓盤移動到by柱 借助to柱 2.將from柱上剩下的那1個圓盤直接移動到to柱 3.將by柱上的movesum 1個圓盤移動到to柱 借助from柱 int sumofplates 4 總的盤子數目 int sum 3 初始時各柱子上盤子數目 enum ...