详解汉诺塔问题

目录

前言

 一、分析

二、C 语言代码实现

三、移动次数

四、宇宙寿命

五、另一个印度传说



前言

汉诺塔(Tower of Hanoi)是一个源于印度古老传说的益智玩具。传说印度教的主神大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上、由大到小按顺序摞着 64 片黄金圆盘。大梵天命令婆罗门把圆盘全部都移到另一根柱子上,并保持原有顺序摞好,而且规定,每次只能移动一个圆盘,并且在移动过程中三根柱子上都始终保持大圆盘在下,小圆盘在上

 一、分析

将三根柱子分别标号为 A、B、C,假设 A 柱上有 n 个圆盘,要把 A 柱上所有的圆盘一个一个移动到 C 柱上,并且移动的过程中三根柱子上都不能出现小圆盘在下,大圆盘在上的情况。

  1. 首先我们需要将 A 柱上面的 n - 1 个圆盘移动到 B 柱上;

  2. 然后把最大的圆盘移动到 C 柱上;

  3. 最后再把 B 柱上的所有圆盘移动到 C 柱上。

在实际操作中,只有第二步可以直接完成,而第一步、第三步又成为移动的新问题。以上的操作实质上是把移动 n 个圆盘的问题转换成移动 n - 1 个圆盘的问题,同理,移动 n - 1 个圆盘的问题又被转换成移动 n - 2 个圆盘的问题,层层递推,最终原问题就转换成了移动 1 个圆盘的问题,而移动 1 个圆盘的操作是可以直接完成的。

二、C 语言代码实现

#include <stdio.h>

int count = 0;  // 用于记录移动圆盘的次数

void move(int n, char x, char y)
{
	printf("将第 %d 个圆盘从 %c 移动到 %c\n", n, x, y);
	count++;
}

// hanoi:将 a 柱上的 n 个圆盘移动到 c 柱上
void hanoi(int n, char a, char b, char c)
{
	if (n == 1)
		move(1, a, c);
	else
	{
		// 1. 首先将 a 柱上面的 n - 1 个圆盘移动到 b 柱上
		hanoi(n - 1, a, c, b);
		// 2. 将第 n 个圆盘,移动到 c 柱上
		move(n, a, c);
		// 3. 将 b 柱上所有的圆盘移动到 c 柱上
		hanoi(n - 1, b, a, c);
	}
}

int main()
{
	int n = 0;
	printf("请输入汉诺塔的层数:>");
	scanf("%d", &n);
	hanoi(n, 'A', 'B', 'C');  // A B C 是三根柱子的编号
	printf("count = %d\n", count);
	return 0;
}

72f1088c42de40d896d6ad711351cb2b.png

2f89dbe3dc1f4033a987761587985a8b.gif

三、移动次数

假设移动圆盘的次数为 h(n),h(1) = 1。因为将 n 个圆盘从 A 柱移到 C 柱,首先要将 A 柱上面的 n - 1 个圆盘移动到 B 柱,这个过程中移动圆盘的次数为 h(n - 1);然后将 A 柱上最大的圆盘,即第 n 个圆盘,移到 C 柱上,这个过程中移动圆盘的次数为 1;最后将 B 柱上的 n - 1 个圆盘移动到 C 柱上,这个过程移动圆盘的次数为 h(n - 1),所以 h(n) = 2h(n - 1) + 1,n > 1。通过证明可以得到 h(n) 的一般式为:h(n) = 2^n - 1(n > 0)

证明(待定系数法)

已知 h(1) = 1,h(n) = 2h(n-1) + 1

设 h(n) + λ = 2(h(n-1) + λ) ==> h(n) = 2h(n-1) + λ,比较系数得 λ = 1,所以有:

h(n) + 1 = 2(h(n-1) + 1),因此数列 {h(n) + 1} 构成以 h(1) + 1 为首项,以 2 为公比得等比数列。

所以有 h(n) + 1 = (h(1) + 1)*2^(n-1),即,h(n) = 2^n - 1

四、宇宙寿命

僧侣们预言,当所有的圆盘都移到另一根柱子上时,世界就将在一声霹雳中消失,梵塔、庙宇和众生也都将同归于尽。假设移动一个圆盘只需要 1 秒钟,那么等到 64 个圆盘全部移动到另一根柱子上时,宇宙被毁灭是什么时候呢?

根据 h(n) = 2^n - 1,宇宙的寿命 = 2^64 - 1(秒),若一年 = 365 * 24 * 60 * 60(秒),则宇宙的寿命 ≈ 5800 亿年。 而据估计,我们的宇宙寿命约为 137.7 亿年。

五、另一个印度传说

和汉诺塔故事相似的,还有另一个印度传说:舍罕王打算奖赏国际象棋的发明人——宰相西萨·班·达依尔。国王问他想要什么,他对国王说:"陛下,请您在这张棋盘的第 1 个小格里赏给我一粒麦子,在第 2 个小格里给 2 粒,第 3 个小格里给 4 粒,以后每一小格都比前一小格加一倍。请您把这样摆满棋盘上所有 64 格的麦粒,都赏给你的仆人吧!" 国王觉得这个要求太容易满足了,就命令给他这些麦粒,当人们把一袋一袋的麦子搬来开始计数时,国王才发现,就是把全印度甚至全世界的麦粒全拿来,也满足不了那位宰相的要求。

宰相要求得到的麦粒到底有多少呢?总数为:1 + 2 + 2^2 + ... + 2^63 = 2^64 - 1,就相当于移完汉诺塔所需要的次数。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值