python求解经典汉诺塔问题、多塔汉诺塔问题

一、经典汉诺塔

汉诺塔是根据一个传说形成的数学问题:
有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
1、每次只能移动一个圆盘;
2、大盘不能叠在小盘上面。
问:如何移?最少要移动多少次?

1、基本想法

f ( n , A , B ) f(n,A,B) f(n,A,B)来表示把n个圆盘从A移到B的方法;
ABC三根杆子,B作为缓存区,n个圆盘从A到C。
分为三步:
1、先把n-1个盘从A移到B, f ( n − 1 , A , B ) f(n-1,A,B) f(n1,A,B)
2、再把第n个圆盘从A移到C, f ( 1 , A , C ) f(1,A,C) f(1,A,C)
3、最后把之前移到B的圆盘从B移到C, f ( n − 1 , B , C ) f(n-1,B,C) f(n1,B,C)
得到 f ( n , A , C ) = f ( n − 1 , A , B ) + f ( 1 , A , C ) + f ( n − 1 , B , C ) f(n,A,C)=f(n-1,A,B)+f(1,A,C)+f(n-1,B,C) f(n,A,C)=f(n1,A,B)+f(1,A,C)+f(n1,B,C)

2、python实现

def f(s,from_,to_,trans_): #输入一个圆盘list,从哪(from_)移到哪(to_),缓存区是哪个(trans_)
	if len(s) == 1:
		return [tuple([s[0],from_,to_])]  #一次移动用元组表示
	else:
		method = f(s[:-1],from_,trans_,to_)+\
		f([s[-1]],from_,to_,trans_)+\
		f(s[:-1],trans_,to_,from_)
		return method   #最后的方法储存在列表里
def main():
	n = int(input('输入圆盘个数:'))
	s = [i+1 for i in range(n)]
	for j in f(s,'A','C','B'):
		print(j)
main()

运行结果示例:
在这里插入图片描述

3、伪可视化

from time import sleep

def f(s,from_,to_,trans_):
	if len(s) == 1:
		return [tuple([s[0],from_,to_])]
	else:
		#注意,f的第一个参数必须是列表
		method = f(s[:-1],from_,trans_,to_)+\
		f([s[-1]],from_,to_,trans_)+\
		f(s[:-1],trans_,to_,from_)
		return method
def pr(n,A,B,C):
	A_,B_,C_ = A[:],B[:],C[:] #副本,不要直接修改A,B,C
	for i in (A_,B_,C_,):
		if len(i) < n:
			for j in range(n-len(i)):
				i.insert(0,'*') #不足n个圆盘的柱子用*填满
	for j in range(n):
		print('{}\t{}\t{}'.format(A_[j],B_[j],C_[j])) #格式化输出一下
def show(n,method,A,B,C):
	pr(n,A,B,C)
	count = 1
	for i in method:
		print('——————————————\n这是第%d次移动:'%count)
		sleep(0.5)
		eval(i[1]).remove(i[0])  #只有一个元素所以可以用remove:(remove删去第一个匹配项)
		eval(i[2]).insert(0,i[0])  #放在最上面,不同append
		pr(n,A,B,C)
		count +=1
def main():
	n = int(input('输入圆盘个数:'))
	s = [i+1 for i in range(n)]
	method = f(s,'A','C','B')
	A = [i+1 for i in range(n)]
	B = []
	C = []
	show(n,method,A,B,C)
main()

运行结果示例:
在这里插入图片描述

二、多个柱子的汉诺塔

1、算法:

(来自WIKI百科
Frame-Stewart 演算法本质上上也是递归的,可简答敘述如下:
f ( n , k ) f(n,k) f(n,k)为在有 k k k个柱子时,移动n个圆盘到另一柱子上至少需要的步数,则:
对于任何移动方法,必定会先将 m ( 1 ≤ m ≤ n − 1 ) {m(1\leq m\leq n-1)} m(1mn1)个圆盘移动到一个中间柱子上,再将第 n n n到第 n − m n-m nm个圆盘通过剩下的 k − 1 k-1 k1个柱子移到目标柱子上,最后将 m m m个在中间柱子上的圆盘移动到目标柱子上。这样所需的操作步数为 2 f ( m , k ) + f ( n − m , k − 1 ) 2f(m,k)+f(n-m,k-1) 2f(m,k)+f(nm,k1)
进行最优化,易得: f ( n , k ) = m i n m ∈ [ 1 , n − 1 ]    ( 2 f ( m , k ) + f ( n − m , k − 1 ) ) f(n,k)={min}_{{m\in [1,n-1]}}\;(2f(m,k)+f(n-m,k-1)) f(n,k)=minm[1,n1](2f(m,k)+f(nm,k1))
显然这里有 f ( n , 2 ) = { 1 if   n=1 ∞ else f(n,2)=\begin{cases} 1&\text{if \ \ n=1} \\ \infty & \text{else} \end{cases} f(n,2)={1if   n=1else

2、如何理解上面的必定

首先任何一种移动方法,必然会 m ( 1 ≤ m ≤ n − 1 ) m(1\leq m\leq n-1) m(1mn1)个移到一个柱子上;
m = 1 m=1 m=1很好理解,视为这一步只移动了一个;
1 ≤ m ≤ n − 1 1\leq m \leq n-1 1mn1,则理解为把m个圆盘按照大小顺序移到另外一个柱子,形如
在这里插入图片描述
变到
在这里插入图片描述

这两种都满足 f ( n , k ) f(n,k) f(n,k)
然后剩下 n − m n-m nm个圆盘,先移动的都是较小的圆盘,故而剩下的 n − m n-m nm个较大的圆盘就只能移动到其余的 k − 1 k-1 k1个柱子上,也就是递归到了在有 k k k个柱子的情况下把 n − m n-m nm个圆盘移到目标柱子的问题。

3、python递归求解次数

数学推导递归出口:
n = 1 n=1 n=1时, f ( 1 , k ) = 1 f(1,k)=1 f(1,k)=1
k = 3 k=3 k=3时,
f ( n , 3 ) = m i n m ∈ [ 1 , n − 1 ]    ( 2 f ( m , 3 ) + f ( n − m , 2 ) ) ( 这 里 注 意 到 f ( n , 2 ) = ∞   : i f   n > 1 ) = 2 f ( n − 1 , 3 ) + f ( 1 , 2 ) = 2 f ( n − 1 , 3 ) + 1 = 2 2 f ( n − 2 , 3 ) + 2 + 1 = … … = 2 n − 1 f ( 1 , 3 ) + ∑ k = 0 n − 2 2 k = 2 n − 1 f(n,3) \\ ={min}_{{m\in [1,n-1]}}\;(2f(m,3)+f(n-m,2)) \\ (这里注意到f(n,2)=\infty \ :if \ n>1)\\ =2f(n-1,3)+f(1,2)\\ =2f(n-1,3)+1\\ =2^2f(n-2,3)+2+1\\ =……\\ =2^{n-1}f(1,3)+\sum_{k=0}^{n-2}2^k\\ =2^{n}-1 f(n,3)=minm[1,n1](2f(m,3)+f(nm,2))f(n,2)= :if n>1=2f(n1,3)+f(1,2)=2f(n1,3)+1=22f(n2,3)+2+1==2n1f(1,3)+k=0n22k=2n1

def f(n,k):
    if  n == 1:#关于n的递归出口
        return 1
    elif k == 3:#关于k的递归出口
        return pow(2,n)-1
    else:
        F = min([2*f(m,k)+f(n-m,k-1) for m in range(1,n)])
        return F
for i in range(1,13):
    for j in range(3,15):
        print('f({},{})={}'.format(i,j,f(i,j)))

运行结果示例:
可以看到 f ( n , k ) f(n,k) f(n,k)随着 k k k的增大最终收敛于 2 n − 1 2n-1 2n1
实际上很容易知道, k ≥ n − 1 k\geq n-1 kn1 f ( n , k ) = 2 n − 1 f(n,k)=2n-1 f(n,k)=2n1,此时的最少移动次数方法为把所有的圆盘依次分别放到不同的柱子上再依次移到目标柱子上。
在这里插入图片描述

3、递推求解移动步骤(在时间复杂度上,递归显然不如递推,不过递推的空间复杂度更高)

def 

待续

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值