转自:http://shmilyaw-hotmail-com.iteye.com/blog/2077098

简介

关于Hanoi塔问题的分析,在网上的文章都写烂了。之所以打算写这篇文章,更多的是针对这个问题相关的各种数学思路和代码实现过程做一个总结。它虽然是一个看似简单的问题,后面引申出来的问题推导方法和思路还是比较丰富的。

问题描述

这个问题起源于一个类似传说故事,在Hanoi这个地方有一个寺庙,这里有3根柱子和64个大小不同的金碟子。每个碟子有一个孔可以穿过。所有的碟子都放在第一个柱子上,而且按照从上到下碟子的大小依次增大的顺序摆设。如下图:

现在,假定寺庙里的僧侣要移动这些碟子,将它们从最左边移动到最右边的柱子上。不过移动的规则如下:

1. 每次只能从一个柱子的最上面移动一个碟子到另外一个柱子上。

2. 不能将大碟子放到小碟子的上面。

按照前面这个规则,我们该怎么去移动这些碟子呢?假定单位时间内可以移动一片碟子,那么最终移动这些碟子到目的柱子需要多长的时间呢?

问题分析

在分析这个问题的时候,我们可以先从一些简单的场景来看怎么来移动碟子保证可以达到目的。假定我们有3个碟子,那么移动它们的过程如下图:

我们假定柱子从左到右分别为a, b, c。从前面移动碟子的步骤可以看到,我们要将a上面的两个碟子先移动到中间的b柱子作为过渡,然后再将最下面的柱子移动到目的c柱子,然后再将上面的两个碟子移过来。在将最下面的碟子移动到c之前,首先的步骤1, 2, 3是将上面的碟子移动到柱子b。而将最下面碟子移动后,上面的两个碟子又要移动一遍,不过是从b移动到c,只是借助的柱子不一样。

所以,从上面的过程,我们可以看到一个可以递归解决问题的思路,如下图:

如图所示,首先我们针对有n个碟子的柱子a,将n-1个碟子移动到柱子b。假定这个问题为S(n)表示移动的步数,则上面的问题是S(n)的一个子问题S(n-1)。这一步对应步骤1。然后将最下面的碟子移动到柱子c,最后再将n-1个碟子移动到c。后面这一步也相当于S(n)的子问题S(n-1)。对应步骤3.它和前面第一步移动n-1个碟子唯一不同的地方在于第一步是借助c将n-1个碟子从a移动到b,而最后这一步是借助a将n-1个碟子从b移动到c。除了借助的柱子和目的柱子不一样,其他的都是一样的。

这样我们就可以很容易得到一个这样的推导关系:

S(n) = 2S(n - 1) +1

再考虑一种初始的情况,假定只有一个碟子需要移动,我们直接将碟子从a移动到c,那么需要的步骤是1步。因此可以说S(1) = 1。

进一步推导

有了前面的归纳关系,我们可以很容易得到如下的一组推导结果:

S(1) = 1

S(2) = 2 x S(1) + 1 = 2 + 1 = 3

S(3) = 2 x S(2) + 1 = 2 x 3 + 1 = 7

S(4) = 2 x S(3) +1 = 2 x 7 + 1 = 15

从这些得出来的结果里,似乎还看不到多少有规律的地方。不过我们可以采取一种根据递推原则代换的方式来尝试发现规律。前面的推导关系里有S(n) = 2 x S(n - 1) +1,那么我们将有如下的推导:

这里,我们发现什么规律没?原来,这里似乎符合如下的一个等式:

当我们最终一路递推到T1的时候,它将满足如下的形式:

这似乎是我们所求得的结果了。当然,这种推导也有可能会出错。最好的情况是我们还需要验证一下它。验证的方法可以考虑用数学归纳法。因为过程比较简单,这里就不再赘述了。最终可以验证出来结果是满足以上等式的。从前面的推导我们可以看出,最终要实现将64个碟子移动到目的柱子,需要的时间是2的64次方这个量级的。在一定程度上,用计算机的内置数据类型都没法表示这个数值。

代码实现

前面的分析可以发现,从计算机实现来说,这个问题是指数函数级别的,意味着它的增长速度非常快,在一定程度上计算机都无法解决。在一个比较小的数值范围内,我们还是可以做一个参考实现的。有了前面的讨论,我们的完整代码实现如下:

 public class Hanoi {  

     public static void move(char a, char b, char c, int n) {
if(n == 0)
return;
move(a, c, b, n-1);
System.out.printf("Move disk %d from %s to %s\n", n, a, c);
move(b, c, a, n-1);
} public static void main(String[] args) {
move('a', 'b', 'c', 3);
}
}

这部分代码里,我们递归的退出条件是当最终碟子移走,即n == 0。运行这一段程序的结果如下:

  1. Move disk 1 from a to c
  2. Move disk 2 from a to b
  3. Move disk 1 from c to a
  4. Move disk 3 from a to c
  5. Move disk 1 from b to c
  6. Move disk 2 from b to a
  7. Move disk 1 from c to b

和前面图示的过程是完全一致的。当然,在提供的数值比较大的时候,我们这种递归的方式就溢出了。

总结

Hanoi塔问题是一个经典的递归问题,它本身的数学复杂度达到了指数函数级别。所以使得运算时间的增长非常快。通过一种递归的思路,首先我们可以总结出一个问题的递归描述方式。然后我们再通过不断的代入和分析,去发现形成等式的规律。这是一种发现递归问题等式描述的方法。为了保证方法最终的正确性,我们还需要经常使用数学归纳法来证明这个等式的正确性。

(转)Hanoi塔问题分析的更多相关文章

  1. 经典递归算法研究:hanoi塔的理解与实现

    关于hanoi塔的原理以及概念,请Google,访问不了去百度. 主要设计到C中程序设计中递归的实现: 主代码实现如下: void hanoi(int src, int dest, int tmp, ...

  2. 栈与递归的实现(Hanoi塔问题等等)

    函数中有直接或间接地调用自身函数的语句,这样的函数称为递归函数.递归函数用 得好,可简化编程工作.但函数自己调用自己,有可能造成死循环.为了避免死循环,要 做到两点: (1) 降阶.递归函数虽然调用自 ...

  3. Hanoi塔问题——递归

    /////////////Hanoi塔问题///////#include<iostream>using namespace std;void hanoi(int i,char A,char ...

  4. Hanoi Tower问题分析

    前言 回家休息第3天了,状态一直不是太好,主要是要补牙,检查身体,见同学见亲戚,心里又着急校招,难得能腾出时间来好好思考,这里也是看<cracking the coding interview& ...

  5. 【题解】Hanoi塔问题

    题目描述 有三根柱A,B,C.在柱A上有N块盘片,所有盘片都是大的在下面,小片能放在大片上面.并依次编好序号,现要将A上的N块片移到C柱上,每次只能移动一片,而且在同一根柱子上必须保持上面的盘片比下面 ...

  6. Hanoi塔问题

    说明:河内之塔(Towers of Hanoi)是法国人M.Claus(Lucas)于1883年从泰国带至法国的,河内为越战时北越的首都,即现在的胡志明市:1883年法国数学家 Edouard Luc ...

  7. Hanoi塔

    2016-03-19 17:01:35 问题描述: 假设有三个命名为 A B C 的塔座 ,在塔座A上插有n个直径大小不相同,由小到大编号为1 ,2 ,3 ,··· ,n的圆盘,要求将A座上的圆盘移至 ...

  8. 汉诺塔(Hanoi)——小小算法

    传送门: 袁咩咩的小小博客 汉诺(Hanoi)塔源于古印度,是非常著名的智力趣题,大意如下: 勃拉玛是古印度的一个开天辟地的神,其在一个庙宇中留下了三根金刚石的棒,第一 根上面套着64个大小不一的圆形 ...

  9. 用函数递归的方法解决古印度汉诺塔hanoi问题

    问题源于印度一个古老传说的益智玩具.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上.并且规 ...

随机推荐

  1. Redis Crackit漏洞防护

    Redis Crackit漏洞利用和防护 注意:本文只是阐述该漏洞的利用方式和如何预防.根据职业道德和<中华人民共和国计算机信息系统安全保护条例>,如果发现的别人的漏洞,千万不要轻易入侵, ...

  2. Jmeter_24个常用函数(分享帖)

    JMeter提供了很多函数,如果能够熟练使用,可以为脚本带来很多方便. JMeter函数是一种特殊值,可用于除测试计划外的任何组件. 函数调用的格式如下所示:${__functionName(var1 ...

  3. 02_Python基本数据类型

    一.什么是数据 数据是描述客观事物的字符(比如95,不同的语义可表示成绩或体重),是计算机可以操作的对象,能够被计算机识别并输入给计算机处理的符号集合. 数据不仅仅包含整形,还包括图像.音乐.视频等非 ...

  4. HTTP协议篇(一):多工、数据流

    管道机制.多工 管道机制(Pipelining) HTTP 1.1 引入了管道机制(Pipelining),即客户端可通过同一个TCP连接同时发送多个请求.如果客户端需要请求两个资源,以前的做法是在同 ...

  5. SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器

    mvc:annotation-driven节点的解析器,是springmvc的核心解析器 官方注释 Open Declaration org.springframework.web.servlet.c ...

  6. BZOJ 2287. [HZOI 2015]疯狂的机器人 [FFT 组合计数]

    2287. [HZOI 2015]疯狂的机器人 题意:从原点出发,走n次,每次上下左右不动,只能在第一象限,最后回到原点方案数 这不煞笔提,组合数写出来发现卷积NTT,然后没考虑第一象限gg 其实就是 ...

  7. BZOJ 2683: 简单题 [CDQ分治]

    同上题 那你为什么又发一个? 因为我用另一种写法又写了一遍... 不用排序,$CDQ$分治的时候归并排序 快了1000ms... #include <iostream> #include ...

  8. Material使用11 核心模块和共享模块、 如何使用@angular/material

    1 创建项目 1.1 版本说明 1.2 创建模块 1.2.1 核心模块 该模块只加载一次,主要存放一些核心的组件及服务 ng g m core 1.2.1.1 创建一些核心组件 页眉组件:header ...

  9. JavaWeb项目架构之NFS文件服务器

    NFS简介 NFS(Network File System)即网络文件系统. 主要功能:通过网络(局域网)让不同的主机系统之间可以共享文件或目录. 主要用途:NFS网络文件系统一般被用来存储共享视频, ...

  10. Angular 向组件传递模板的几种方法

    最近在写一个日期选择器组件,为了满足将来可能出现的各种需求,所以需要能够高度的自定义组件的样式.为了达到这个目的,需要能够在日期选择器组件外控制每个日期格子内要显示的内容,比如,标上节假日之类的.这时 ...