算法:汉诺塔问题(Tower of Brahma puzzle)
一、算法背景
最早发明这个问题的人是法国数学家爱德华·卢卡斯。传说越南河内某间寺院有三根银棒(A, B, C),上串 64 个金盘。 寺院里的僧侣依照一个古老的预言,以上述规则移动这些盘子;预言说当这些盘子移动完毕,世界就会灭亡。 这个传说叫做梵天寺之塔问题(Tower of Brahma puzzle)。但不知道是卢卡斯自创的这个传说,还是他受他人启发。

A C B
二、汉诺塔算法
有三根杆子A,B,C。A 杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
- 每次只能移动一个圆盘;
- 大盘不能叠在小盘上面。
提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。
问:如何移?最少要移动多少次?
解法的基本思想是递归。假设有 A、B、C 三个塔,A 塔有 块盘,目标是把这些盘全部移到 C 塔。那么先把 A 塔顶部的
块盘移动到 B 塔,再把 A 塔剩下的大盘移到 C,最后把 B 塔的
块盘移到 C。如此递归地使用下去, 就可以求解。
1 /**
2 * Java递归函数解决河内塔之难题
3 *
4 * @param n 移动物体数量
5 * @param fromRod 原先放的位置
6 * @param toRod 要放去的位置
7 * @param auxRod 中间留空方便转移的位置
8 */
9 private static void towerOfHanoi(int n, char fromRod, char toRod, char auxRod) {
10 count++;
11 if (n == 1) { // 递归出口
12 System.out.println("Move disk 1 from rod " + fromRod + " to rod " + toRod);
13 return;
14 }
15 towerOfHanoi(n-1, fromRod, auxRod, toRod);
16 System.out.println("Move disk " + n + " from rod " + fromRod + " to rod " + toRod); // 移动
17 towerOfHanoi(n-1, auxRod, toRod, fromRod);
18 }
设移动 n 个盘子的汉诺塔问题需要 towerOfHanoi(n) 次移动操作来完成。由展示移动过程算法可知 towerOfHanoi(n) 应是三部分之和。
(1) 将 n 个盘上面的 n-1 个盘子借助 C 桩从 A 桩移到 B 桩上,需 towerOfHanoi(n-1) 次移动;
(2) 然后将 A 桩上第 n 个盘子移到 C 桩上(1次);
(3) 最后,将 B 桩上的 n-1 个盘子借助 A 桩移到 C 桩上,需 towerOfHanoi(n-1)次。
因而有递归关系: towerOfHanoi(n) = 2 * towerOfHanoi(n-1) + 1,初始条件(递归出口): towerOfHanoi(1) = 1,即 1、3、7、15、31…… 移动次数 count = towerOfHanoi(n) = 2n -1 ,时间复杂度为 O(2n)。
本文源代码:
1 package algorithm;
2
3 /**
4 * 汉诺塔问题(Tower of Brahma puzzle)
5 *
6 * 最早发明这个问题的人是法国数学家爱德华·卢卡斯。
7 *
8 * 传说越南河内某间寺院有三根银棒,上串 64 个金盘。
9 * 寺院里的僧侣依照一个古老的预言,以上述规则移动这些盘子;预言说当这些盘子移动完毕,世界就会灭亡。
10 * 这个传说叫做梵天寺之塔问题(Tower of Brahma puzzle)。
11 * 但不知道是卢卡斯自创的这个传说,还是他受他人启发。
12 *
13 * 若传说属实,僧侣们需要 2^{64}-1 步才能完成这个任务;
14 * 若他们每秒可完成一个盘子的移动,就需要 5849 亿年才能完成。整个宇宙现在也不过 137 亿年。
15 *
16 * 这个传说有若干变体:寺院换成修道院、僧侣换成修士等等。
17 * 寺院的地点众说纷纭,其中一说是位于越南的河内,所以被命名为“河内塔”。
18 * 另外亦有“金盘是创世时所造”、“僧侣们每天移动一盘”之类的背景设定。
19 */
20 public class TowerOfHanoi {
21 /* 移动次数 */
22 private static int count = 0;
23
24 /**
25 * Java递归函数解决河内塔之难题
26 *
27 * @param n 移动物体数量
28 * @param fromRod 原先放的位置
29 * @param toRod 要放去的位置
30 * @param auxRod 中间留空方便转移的位置
31 */
32 private static void towerOfHanoi(int n, char fromRod, char toRod, char auxRod) {
33 count++;
34 if (n == 1) {
35 System.out.println("Move disk 1 from rod " + fromRod + " to rod " + toRod);
36 return;
37 }
38 towerOfHanoi(n-1, fromRod, auxRod, toRod);
39 System.out.println("Move disk " + n + " from rod " + fromRod + " to rod " + toRod);
40 towerOfHanoi(n-1, auxRod, toRod, fromRod);
41 }
42
43 public static void main(String[] args) {
44 int n = 4;
45 towerOfHanoi(n, 'A', 'C', 'B');
46 /**
47 * 可见移动次数是 (2^n-1), 其时间复杂度O(2^n)。
48 *
49 * 一天24h, 一小时60min, 一分钟60s, 一天有 24*60*60 = 86400(s), 一年平均365天来算,一年有 3153,6000 (s)
50 * 可见要几千亿年才能移动完, 这个算法用于移动大数量小空间的物体肯定是行不通的。
51 *
52 * 这个算法主要考验人的解决问题、逻辑思想, 以及计算推理的能力
53 */
54 System.out.println("移动次数:" + count);
55 }
56 }
算法:汉诺塔问题(Tower of Brahma puzzle)的更多相关文章
- 汉诺塔 Hanoi Tower
电影<猩球崛起>刚开始的时候,年轻的Caesar在玩一种很有意思的游戏,就是汉诺塔...... 汉诺塔源自一个古老的印度传说:在世界的中心贝拿勒斯的圣庙里,一块黄铜板上插着三支宝石针.印度 ...
- python算法-汉诺塔问题
汉诺塔问题 初始状态: 思考:当盘子的个数是3的时候,大家写出移动顺序 移动的步骤: 3个盘子,从a到c 1.前面两个盘子,从a到b 1)把前面一个盘子,从a到c a->c 2)把第二个盘子 ...
- C++汉诺塔递归实现
程序背景: 汉诺塔(Tower of Hanoi)又称河内塔,问题是源于印度一个古老传说的益智玩具.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命 ...
- 汉诺塔(河内塔)算法 ----C语言递归实现
汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具.大梵天创造世界的时候做了三根金刚石柱子, 在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆罗门把圆盘从下面开始按大小顺 ...
- 汉诺塔算法详解之C++
汉诺塔: 有三根杆子A,B,C.A杆上有N个(N>1)穿孔圆环,盘的尺寸由下到上依次变小.要求按下列规则将所有圆盘移至C杆: 每次只能移动一个圆盘: 大盘不能叠在小盘上面. 提示:可将圆盘临时置 ...
- 汉诺塔算法的递归与非递归的C以及C++源代码
汉诺塔(又称河内塔)问题其实是印度的一个古老的传说. 开天辟地的神勃拉玛(和中国的盘古差不多的神吧)在一个庙里留下了三根金刚石的棒,第一根上面套着64个圆的金片,最大的一个在底下,其余一个比一 个小, ...
- [js - 算法可视化] 汉诺塔(Hanoi)演示程序
前段时间偶然看到有个日本人很早之前写了js的多种排序程序,使用js+html实现的排序动画,效果非常好. 受此启发,我决定写几个js的算法动画,第一个就用汉诺塔. 演示地址:http://tut.ap ...
- C语言之算法初步(汉诺塔--递归算法)
个人觉得汉诺塔这个递归算法比电子老鼠的难了一些,不过一旦理解了也还是可以的,其实网上也有很多代码,可以直接参考.记得大一开始时就做过汉诺塔的习题,但是那时代码写得很长很长,也是不理解递归的结果.现在想 ...
- what' the python之递归函数、二分算法与汉诺塔游戏
what's the 递归? 递归函数的定义:在函数里可以再调用函数,如果这个调用的函数是函数本身,那么就形成了一个递归函数. 递归的最大深度为997,这个是程序强制定义的,997完全可以满足一般情况 ...
随机推荐
- Vue组件传值(一)之 父子之间如何传值
Vue中组件之间是如何实现通信的? 1.父传子: 父传子父组件通过属性进行传值,子组件通过 props 进行接受: 1 父组件中: 2 3 <template> 4 <div id= ...
- CodeForce-812B Sagheer, the Hausmeister(DFS)
Sagheer, the Hausmeister CodeForces - 812B 题意:有一栋楼房,里面有很多盏灯没关,为了节约用电小L决定把这些灯都关了. 这楼有 n 层,最左边和最右边有楼梯. ...
- 自己实现一个Controller——终极型
经过前两篇的学习与实操,也大致掌握了一个k8s资源的Controller写法了,如有不熟,可回顾 自己实现一个Controller--标准型 自己实现一个Controller--精简型 但是目前也只能 ...
- .Net Core with 微服务 - 分布式事务 - 可靠消息最终一致性
前面我们讲了分布式事务的2PC.3PC , TCC 的原理.这些事务其实都在尽力的模拟数据库的事务,我们可以简单的认为他们是一个同步行的事务.特别是 2PC,3PC 他们完全利用数据库的事务能力,在一 ...
- composer出现问题: Could not open input file: composer.phar
可以执行下面命令 php -r "readfile('https://getcomposer.org/installer');" | php This will install c ...
- CF585E-Present for Vitalik the Philatelist【莫比乌斯反演,狄利克雷前缀和】
正题 题目链接:https://www.luogu.com.cn/problem/CF585E 题目大意 给出一个大小为\(n\)的可重集\(T\),求有多少个它的非空子集\(S\)和元素\(x\)满 ...
- Hutool-Convert类型转换常见使用
Convert 主要针对于java中常见的类型转化 java常见类型的转化 转化为字符串 public class HConvert { public static void main(String[ ...
- 【MySQL】MySQL进阶(外键约束、多表查询、视图、备份与恢复)
约束 外键约束 外键约束概念 让表和表之间产生关系,从而保证数据的准确性! 建表时添加外键约束 为什么要有外键约束 -- 创建db2数据库 CREATE DATABASE db2; -- 使用db2数 ...
- Spring源码之创建AOP代理之增强器的获取
前言 在上一篇博文中我们说到了通过自定义配置完成了对AnnotationAwareAspectJAutoProxyCreator类型的自动注册,那么这个类究竟做了什么工作从而完成AOP的操作呢?首先我 ...
- enum 试图表达64位数
enum AttributeType: unsigned long long{ aa = 1, bb = 2, cc = 0x842AC1040000}; int main() { DWORD64 b ...