漢諾塔遞歸調用(C語言實現)有三根相鄰的柱子,標號爲A,B,C,A柱子上從下到上按金字塔狀疊放着n個不同大小的圓盤,要把所有盤子一個一個移動到柱子B上,並且每次移動同一根柱子上都不能出現大盤子在小盤

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

首先我們肯定是把上面n-1個盤子移動到柱子C上,然後把最大的一塊放在B上,最後把C上的所有盤子移動到B上,由此我們得出表達式:
H⑴ = 1
H(n) = 2H(n-1)+1 (n>1)
那麼我們很快就能得到H(n)的一般式:
H(n) = 2^n - 1 (n>0)
並且這種方法的確是最少次數的,證明非常簡單,可以嘗試從2個盤子的移動開始證,你可以試試。
進一步加深問題(解法原創
_):
假如現在每種大小的盤子都有兩個,並且是相鄰的,設盤子個數爲2n,問:⑴假如不考慮相同大小盤子的上下要多少次移動,設移動次數爲J(n);⑵只要保證到最後B上的相同大小盤子順序與A上時相同,需要多少次移動,設移動次數爲K(n)。
⑴中的移動相當於是把前一個問題中的每個盤子多移動一次,也就是:
J(n) = 2
H(n) = 2*(2^n - 1) = 2^(n+1)-2
在分析⑵之前
,我們來說明一個現象,假如A柱子上有兩個大小相同的盤子,上面一個是黑色的,下面一個是白色的,我們把兩個盤子移動到B上,需要兩次,盤子順序將變成黑的在下,白的在上,然後再把B上的盤子移動到C上,需要兩次,盤子順序將與A上時相同,由此我們歸納出當相鄰兩個盤子都移動偶數次時,盤子順序將不變,否則上下顛倒。
現在回到最開始的問題,n個盤子移動,上方的n-1個盤子總移動次數爲2H(n-1),所以上方n-1個盤子的移動次數必定爲偶數次,最後一個盤子移動次數爲1次。
討論問題⑵,
綜上兩點,可以得出,要把A上2n個盤子移動到B上,首先可以得出上方的2n-2個盤子必定移動偶數次,所以順序不變,移動次數爲:
J(n-1) = 2^n-2
然後再移動倒數第二個盤子,移動次數爲2
J(n-1)+1 = 2^(n+1)-3,
最後移動最底下一個盤子,所以總的移動次數爲:
K(n) = 2*(2J(n-1)+1)+1 = 2(2^(n+1)-3)+1 = 2^(n+2)-5
開天闢地的神勃拉瑪(和中國的盤古差不多的神吧)在一個廟裏留下了三根金剛石的棒,第一根上面套着64個圓的金片,最大的一個在底下,其餘一個比一個小,依次疊上去,廟裏的衆僧不倦地把它們一個個地從這根棒搬到另一根棒上,規定可利用中間的一根棒作爲幫助,但每次只能搬一個,而且大的不能放在小的上面。計算結果非常恐怖(移動圓片的次數)大約是1.84467440*10^19,衆僧們即便是耗盡畢生精力也不可能完成金片的移動了。
算法介紹
其實算法非常簡單,當盤子的個數爲n時,移動的次數應等於2^n – 1(有興趣的可以自己證明試試看)。後來一位美國學者發現一種出人意料的簡單方法,只要輪流進行兩步操作就可以了。首先把三根柱子按順序排成品字型,把所有的圓盤按從大到小的順序放在柱子A上,根據圓盤的數量確定柱子的排放順序:若n爲偶數,按順時針方向依次擺放 A B C;
若n爲奇數,按順時針方向依次擺放 A C B。
⑴按順時針方向把圓盤1從現在的柱子移動到下一根柱子,即當n爲偶數時,若圓盤1在柱子A,則把它移動到B;若圓盤1在柱子B,則把它移動到C;若圓盤1在柱子C,則把它移動到A。
⑵接着,把另外兩根柱子上可以移動的圓盤移動到新的柱子上。即把非空柱子上的圓盤移動到空柱子上,當兩根柱子都非空時,移動較小的圓盤。這一步沒有明確規定移動哪個圓盤,你可能以爲會有多種可能性,其實不然,可實施的行動是唯一的。
⑶反覆進行⑴⑵操作,最後就能按規定完成漢諾塔的移動。
所以結果非常簡單,就是按照移動規則向一個方向移動金片:
如3階漢諾塔的移動:A→C,A→B,C→B,A→C,B→A,B→C,A→C
漢諾塔問題也是程序設計中的經典遞歸問題,下面看一下源碼。

// An highlighted block
#include <stdio.h>
#include <string.h>
/*
 解題思路:1將 n-1個盤子先放到B座位上
          2.將A座上地剩下的一個盤移動到C盤上
          3、將n-1個盤從B座移動到C座上
*/
//函數原型聲明
void move(char x, char y);
void hannuo(int n,char one ,char two,char three)
{
  void move(char x,char y);
  if(n==1)move(one, three); //如果此時one就一個盤子,就直接移動到three上
  else
{
  hannuo(n-1,one ,three,two); //否則就在One的基礎上留下最後一個盤子,One藉助three移動到two上
  move(one,three);  //在把one裏最後一個大盤子移動到three上邊
  hannuo(n-1,two,one,three);  //最後把two裏的盤子,藉助one依次放進three裏
 
}
}
void move(char x,char y)
{
 printf("%c--->%c",x,y)  //直接輸出一下
}
 
void main()
{
 int n;
printf("input your number");  //接收輸入
scanf("%d",&n);
hannuo(n,'A','B','C');  //在這裏調用函數
return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章