根据古印度神话,在贝拿勒斯的圣庙里安放着一个铜板,板上插有三根一尺长的宝石针。印度教的主神梵天在创造世界的时候,在其中的一根针上摆了由小到大共64片中间有孔的金片。无论白天和黑夜,都有一位僧侣负责移动这些金片,规则是一次只能将一片金片移到另一根针上,而且在任何时候以及一根针上,小片永远在大片的上面。当所有的64片金片都由那根针移到另一根针上时,这个世界就在霹雳中消失。
记得初中的时候玩过这个游戏,但是没仔细去研究。看来出来混迟早是要还的。作为第一个递归入门问题,还是写下来好好纪念一下。
1)把问题抽象出来。用字符A、B、C来表示三根针,这梵塔问题可以表示成下列如图:
(图片来源于网络)
把A中所有的金片移到C柱上(移动到B、C柱上均可,假设我们移到C柱上)。
按照简单到复杂的思路,先考虑A上只有一个金片,我们只需把A上的金片直接移到C处即可。当A柱上的金片N>1时,我们需要借助辅助柱B来实现。进一步思考,当我们只移动最小的金片至次大的金片(即第一个到第N-1个金片),完全不会受到第N个金片的影响。所以自然想到可以用某种循环的步骤——递归,来实现它。
所以我们把从A移动N个金片到C这一过程分成如下步骤:
(1)把A上的N-1个金片移动到B。
(2)把A上的最后一个金片移动到C。
(3)把B上N-1个金片移动到C。
步骤(1)、(3)类似,但是不是完全相同。我们可以把(1)进一步细分:
1.把A上N-2个金片移动到C柱。(和我们最外层的过程一致。)
2.把第N-1个金片移动到B。
3.把C柱上N-2个金片移动到B。
我一直认为递归的关键是找出核心的循环步骤,就像找周期函数的周期一样。还有一点就是解决第N个问题和第N-1个问题之间不受影响。(第二点有待考证,只是一种感觉)。
下面是代码:
//test hanoi
#include<iostream>
using namespace std;
const int N = 3; //设置初始A柱上的金片数目
void move(char from,char to) //定义一个move函数来表示把一个金片从一个柱移动到另一个柱
{
cout<<"From"<<from<<"to"<<to<<endl;
}
void hanoi(int n,char p1,char p2, char p3) //定义函数hanoi表示把p1上的N个金片移动到p3上
{
if(n==1)
{
move(p1,p3);
}else{
hanoi(n-1,p1,p3,p2);
move(p1,p3);
hanoi(n-1,p2,p1,p3);
}
}
int main()
{
hanoi(N,'A','B','C');
return 0;
}
下面来考虑一下把N个金片从A柱移动到C柱一共移动了多少步:
设把A柱上的N-1个金片移动到B柱用了An-1步,把第N个金片从A柱移动到C柱用了1步。再把B柱上N-1个金片移动到C柱也用了An-1步,所以一共用了An = 2*An-1 +1. ( A1 =1).下面就是一个数列问题。An+1 =2*(An-1 +1);等比数列搞定它。得到An=2^n -1.