[Python3 练习] 006 汉诺塔2 非递归解法
题目:汉诺塔 II
- 接上一篇 [Python3 练习] 005 汉诺塔1 递归解法
- 这次不使用递归
- 不限定层数
(1) 解决方式
- 利用“二进制”
(2) 具体说明
- 统一起见
- 我把左、中、右三根柱子依次称为 A 塔、B 塔、C 塔
- 金片默认都在 A 塔
- n 片金片从小到大依次编号为 0 号、1 号、……、n-1 号
1) 举个“栗子”
- 假设有一个 4 层高的汉诺塔,设初始值为 0000(2)
- 按 "8"、"4"、"2"、"1" 称呼二进制的各位
- "8"位、"4"位、"2"位、"1"位依次对应 3 号金片、2 号金片、1号金片、0 号金片
- 如图
- 开始累加,每次加 1
- 0000(2) -> 0001(2)
- "1"位由 0 变 1,则将 0 号金片右移,即将 0 号金片由 A 塔移至 B塔
- 补充:若要将 C 塔上的金片右移,则移至 A 塔,三个塔是循环的
- 如图
- 0001(2) -> 0010(2)
- 产生进位;进到哪位,则移动该位对应的金片
- 此时进位至"2"位,则右移 1 号金片
- 右移 1 号金片时,因为 1 号金片不能放在 B 塔的 0 号金片上方,所以继续向右走,C 塔正好符合要求
- 如图
- 0010(2) -> 0011(2)
- "1"位由 0 变 1,则将 B 塔的 0 号金片右移至 C 塔
- 如图
- 0011(2) -> 0100(2)
- 产生进位,此时进至“4”位,则将 A 塔上的 2 号金片右移至 B 塔
- 如图
- 0100(2) -> 0101(2)
- 个位由 0 变 1,则将 C 塔的 0 号金片右移至 A 塔
- 如图
……
- 按这个方法进行下去,当数字变成 "1111" 时,A 塔的 4 片金片就都在 C 塔上了
2) 一些说明
此“二进制”方法可以解决汉诺塔,但奇数金片与偶数金片在结果上有些许不同
- 按照上面的规则,奇数金片最终会移至 B 塔,偶数金片最终会移至 C 塔
- 可借高数中“轮换对称性”的思想,在面对奇数金片时,把原来的 B 塔看成 C 塔,把原来的 C 塔看成 B 塔
上面的操作有 2 个规律
- 规律一
- 因为每走一步,数值加 1,所以该二进制数即为步数
- 该二进制数末尾 0 的个数对应要移动的金片编号
- 没有 0,即为 0 个 0,对应 0 号金片;可回顾图 "0001"、"0011"、"0101"
- 1 个 0,对应 1 号金片;可回顾图 "0010"
- 2 个 0,对应 2 号金片;可回顾图 "0100"
- 依此类推
- 规律二
- 编号为 0、2、4……的金片,总是进行右移操作
- 编号为 1、3、5……的金片,总是进行左移操作
- 三根柱子,右移 2 格即为左移 1 格
- 规律一
3) 计算移动次数
- 按递归的思路,汉诺塔可分成三大步
- 将 A 塔的上面 n-1 片金片移至 B 塔
- 将 A 塔剩余的 1 片金片移至 C 塔
- 将 B 塔的 n-1 片金片移至 C 塔
- 设 f(n) 为 n 片金片完成移动需要的最少次数,则 f(n) = f(n-1) + 1 + f(n-1),即 f(n) = 2f(n-1) + 1
- 若只有 1 片金片,则 f(1) = 1
- 若有 2 片金片,则 f(2) = 3
- 若有 3 片金片,则 f(3) = 7
- 照此规律,可假设 f(n) = 2n - 1
- 莫名想到线代中用的“第一类数学归纳法”,我献丑证一下,算是温故知新
- 证明 f(n) = 2n - 1 成立:
- 当 n = 1 时,f(1) = 21 - 1 = 1,成立
- 当 n = k 时,设 f(k) = 2k - 1 成立
- => 当 n = k + 1 时,f(k+1) = 2f(k) + 1 = 2 * (2k - 1) + 1 = 2k+1 - 1,满足假设
- => 汉诺塔的移动次数为 f(n) = 2n - 1,证毕
(3) 程序
1) 代码
# 不使用递归
def hanoi(n):
tower_belong = [0] * n # 用列表开辟 n 个空间,用于存放 n 个金片各自的编号,编号对应塔号
# 金片移动,编号对应更改
if n % 2 == 0: # 金片数量不同,塔的排序不同
tower_name = ['A', 'B', 'C'] # 若 n 为偶数,最终所有金片恰好能移到右塔
else:
tower_name = ['A', 'C', 'B'] # 若 n 为奇数,最终所有金片会移到中塔
# 用“轮换对称”将 B、C 两塔互换名字,以实现“负负得正”
for step in range(1, 2**n): # n 片金片最少需要移动 2^n - 1 次
bin_step = bin(step) # 求得 step 的二进制数值
gold_num = len(bin_step) - bin_step.rfind('1') - 1
# 计算 step 末尾 0 的个数,得到金片编号;上面说的“规律一”
# 如 step = 0b0001,则 step 末尾 0 的个数为 0,表示此刻应移动 0 号金片
# 如 step = 0b0100,则 step 末尾 0 的个数为 2,表示此刻应移动 2 号金片,依此类推
# rfind 是从 0 开始计数,所以再减个 1
print(f"第 {step:2} 步:移动 {str(gold_num)} 号金片,从 {tower_name[tower_belong[gold_num]]} 塔到", end=' ') # 移出金片的塔
if gold_num % 2 == 0: # 若 num 为 偶数,则右移
tower_belong[gold_num] = (tower_belong[gold_num] + 1) % 3
# 若从 C 塔右移,则又回到了 A 塔
else: # 若 num 为奇数,则左移
tower_belong[gold_num] = (tower_belong[gold_num] + 2) % 3
# 若从 A 塔左移,则又去到了 C 塔
print(tower_name[tower_belong[gold_num]], '塔') # 移入金片的塔
# 清爽、无注释版
def hanoi(n):
tower_belong = [0] * n
if n % 2 == 0:
tower_name = ['A', 'B', 'C']
else:
tower_name = ['A', 'C', 'B']
for step in range(1, 2**n):
bin_step = bin(step)
gold_num = len(bin_step) - bin_step.rfind('1') - 1
print(f"第 {step:2} 步:移动 {str(gold_num)} 号金片,从 {tower_name[tower_belong[gold_num]]} 塔到", end=' ')
if gold_num % 2 == 0:
tower_belong[gold_num] = (tower_belong[gold_num] + 1) % 3
else:
tower_belong[gold_num] = (tower_belong[gold_num] + 2) % 3
print(tower_name[tower_belong[gold_num]], '塔')
2) 运行情况
- 3 层汉诺塔
- 4 层汉诺塔
[Python3 练习] 006 汉诺塔2 非递归解法的更多相关文章
- PTA 汉诺塔的非递归实现(C 语言)
借助堆栈以非递归(循环)方式求解汉诺塔的问题(n, a, b, c), 即将N个盘子从起始柱(标记为“a”)通过借助柱(标记为“b”)移动到目标柱(标记为“c”), 并保证每个移动符合汉诺塔问题的要求 ...
- C#中汉诺塔问题的递归解法
百度测试部2015年10月份的面试题之——汉诺塔. 汉诺塔就是将一摞盘子从一个塔转移到另一个塔的游戏,中间有一个用来过度盘子的辅助塔. 百度百科在此. 游戏试玩在此. 用递归的思想解决汉诺塔问题就是分 ...
- [Python3 练习] 005 汉诺塔1 递归解法
题目:汉诺塔 I (1) 描述 传说,在世界中心贝拿勒斯(在印度北部)的圣庙外有左中右三根足够长的柱子(塔) 左边柱子上套着 64 片金片,金片按"上小下大"排,其余两根是空柱子 ...
- 汉诺塔算法的递归与非递归的C以及C++源代码
汉诺塔(又称河内塔)问题其实是印度的一个古老的传说. 开天辟地的神勃拉玛(和中国的盘古差不多的神吧)在一个庙里留下了三根金刚石的棒,第一根上面套着64个圆的金片,最大的一个在底下,其余一个比一 个小, ...
- python汉诺塔问题的递归理解
一.问题背景 汉诺塔问题是源于印度一个古老传说. 源于印度一个古老传说的益智玩具.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆罗门把圆盘从下 ...
- 理解 Hanoi 汉诺塔非递归算法
汉诺塔介绍: 汉诺塔(港台:河内塔)是根据一个传说形成的数学问题: 最早发明这个问题的人是法国数学家爱德华·卢卡斯. 传说越南河内某间寺院有三根银棒,上串 64 个金盘.寺院里的僧侣依照一个古老的预言 ...
- 汉诺塔算法c++源代码(递归与非递归)[转]
算法介绍: 其实算法非常简单,当盘子的个数为n时,移动的次数应等于2^n - 1(有兴趣的可以自己证明试试看).后来一位美国学者发现一种出人意料的简单方法,只要轮流进行两步操作就可以了.首先把三根柱 ...
- c++迭代递归实现汉诺塔(5种迭代方法满足你)
#include <iostream> //从A到C using namespace std; int n; void ready() { cout << "请输入汉 ...
- 汉诺塔问题C++实现
大家好,我是小鸭酱,博客地址为:http://www.cnblogs.com/xiaoyajiang 以下进行汉诺塔问题的递归实现 #include <iostream.h> int gb ...
随机推荐
- 洛谷 P2622 关灯问题II (状态压缩+BFS)
题目描述 现有n盏灯,以及m个按钮.每个按钮可以同时控制这n盏灯--按下了第i个按钮,对于所有的灯都有一个效果.按下i按钮对于第j盏灯,是下面3中效果之一:如果a[i][j]为1,那么当这盏灯开了的时 ...
- JS深度比较两个对象是否相等
/** * 深度比较两个对象是否相等 * @type {{compare: compareObj.compare, isObject: (function(*=): boolean), isArray ...
- java 中的编码
1.1字节=8位,1024字节=1KB2.16进制0x12345678,其二进制为00010010 00110100 01010110 01111000共4字节3.字节序:两个或多个字节存放的先后顺序 ...
- bzoj3091 城市旅行 LCT + 区间合并
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=3091 题解 调了整个晚自习才调出来的问题. 乍一看是个 LCT 板子题. 再看一眼还是个 LC ...
- node项目实战-用node-koa2-mysql-bootstrap搭建一个前端论坛
前言 在学习了koa2和express并写了一些demo后,打算自己写一个项目练练手,由于是在校生,没什么好的项目做,即以开发一个前端论坛为目标,功能需求参照一下一些社区拟定,主要有: 登录注册 个人 ...
- spring security基本知识(二) 自定义认证
配置自定义的用户存储 我们在 SecurityConfig 的配置类中 重写了 configure(AuthenticationManagerBuilder auth) 方法,我们可以通过 Authe ...
- 并行操作多个序列map
>>> def add1(a): return a + 1 >>> def add2(a,b): return a + b >>> def add ...
- 【LuoguP3329&4123】[ZJOI2011]最小割&[CQOI2016]不同的最小割
链接1 链接2 题意简述 第一个题 : 问图中有多少不同的最小割数值 第二个题 : \(q\) 次询问图中多少对点对之间的最小割小于 \(x\) . Sol 两个都是模板题就放一起了. 求完最小割树直 ...
- 2016.5.21【初中部 NOIP提高组】模拟赛A 总结
这次比赛的题目看上去好像不难,但当开始仔细想的时候才发现,并没有那么简单. T1旅行:刚开始看到k<=4的时候还以为有题可以AC了,不过呢,还是毫无思路. T3Pty爬山:雨天的尾巴最近打了几道 ...
- LeetCode--096--不同的二叉搜索树(python)
我的思路比较low直接看官方题解吧... class Solution: def numTrees(self, n: int) -> int: G = [0] * (n+1) G[0],G[1] ...