用递归算法的来解决汉诺塔问题

转自:https://blog.csdn.net/xb2355404/article/details/79144451 

汉诺塔

汉诺塔是一个发源于印度的益智游戏,也叫河内塔。相传它源于印度神话中的大梵天创造的三个金刚柱,一根柱子上叠着上下从小到大64个黄金圆盘。大梵天命令婆罗门将这些圆盘按从小到大的顺序移动到另一根柱子上,其中大圆盘不能放在小圆盘上面。当这64个圆盘移动完的时候,世界就将毁灭。 
汉诺塔游戏 
汉诺塔问题源于印度神话 
那么好多人会问64个圆盘移动到底会花多少时间?那么古代印度距离现在已经很远,这64个圆盘还没移动完么?我们来通过计算来看看要完成这个任务到底要多少时间? 
我们首先利用数学上的数列知识来看看F(n=1)=1,F(n=2)=3,F(n=3)=7,F(n=4)=15……F(n)=2F(n-1)+1; 
我们使用数学归纳法可以得出通项式:F(n)=2^n-1。当n为64时F(n=64)=18446744073709551615。 
我们假设移动一次圆盘为一秒,那么一年为31536000秒。那么18446744073709551615/31536000约等于584942417355天,换算成年为5845.54亿年。 
目前太阳寿命约为50亿年,太阳的完整寿命大约100亿年。所以我们整个人类文明都等不到移动完整圆盘的那一天。

汉诺塔问题算法

有很多人对汉诺塔的解法产生了兴趣。从一阶汉诺塔到N阶汉诺塔它们是否有规律性的算法? 
我们在使用程序实现它之前我们来分析分析汉诺塔的解法: 
我们设定三个柱子A,B,C。我们的目的是将环从A–>C。 
这里写图片描述
当N=1即一阶时它的路径很简单只需要从A->C进行移动。 
当N=2时我们需要进行三步: 
这里写图片描述 
这里我制作了一个动图来演示了过程。当然N=3时中共7步8帧,由于繁琐的制图我就不继续使用动图演示了! 
3阶汉诺塔其实我们都可以轻松的解决。 
那么到底他们有什么共性呢?或者说和递归有什么联系呢? 
我们还是来使用图片解释: 
acb 
左图为2阶汉诺塔中间的步骤之一,我们已经将小环移动到了B柱,最大环此时可以视为不存在。那么如右图所示我们将B,C柱子交换位置,那么此步骤是否和移动1阶汉诺塔一样了呢? 
然后我们中间执行了将最大环从A移动到C的固定步骤 
bac 
同理,在左图为2阶汉诺塔中间的步骤之一,我们已经将最大的环移动到了C柱,最大环此时可以视为不存在。那么如右图所示我们将A,B柱子交换位置,那么接下来的步骤是否和移动1阶汉诺塔一样了呢? 
到这里我们总结出了如下特点: 
其实2阶汉诺塔相当于执行了三大步骤: 
1.在ACB的顺序下执行了一阶汉诺塔的移法 
2.从A->C移动了最大盘 
3.在BAC的顺序下执行了一阶汉诺塔的移法
 
那么推广到三阶的时候,我们将小环和中环视为一个整体,我们是否又变成了执行二阶汉诺塔方法了呢? 
那么四阶前三个环视为整体,五阶前四个环视为整体……我们已经找到了解决汉诺塔方法的递归算法。下面,我们就用代码来实现它。

代码实现

这里的关键在于对三个关键步骤的代码实现。

public class Hanoilmpl {


    public void hanoi(int n, char A, char B, char C) {
        if (n == 1) {
            move(A, C);
        } else {
            hanoi(n - 1, A, C, B);//步骤1 按ACB数序执行N-1的汉诺塔移动
            move(A, C);             //步骤2   执行最大盘子移动
            hanoi(n - 1, B, A, C);//步骤3 按BAC数序执行N-1的汉诺塔移动
        }
    }

    private void move(char A, char C) {//执行最大盘子的从A-C的移动
        System.out.println("move:" + A + "--->" + C);
    }

    public static void main(String[] args) {
        Hanoilmpl hanoi = new Hanoilmpl();
        System.out.println("移动汉诺塔的步骤:");
        hanoi.hanoi(3, 'a', 'b', 'c');
    }
}
  • 执行结果: 
    结果

关于递归的四条基本法则

1.基准情形。必须有某些基准情形,它无需递归就能解出。 
2.不断推进。对于那些需要递归求解的情形,每一次递归调用都必须要使求解的状况朝接近基准情形的方向推进。 
3.设计法则。假设所有的递归调用都能运行。 
4.合成效益法则。在求解同一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作 
——-摘自《数据结构与算法分析(机械工业出版社 Mark Allen Weiss著)》

猜你喜欢

转载自blog.csdn.net/shenaisi/article/details/81271279