🧡🧡实验内容🧡🧡
汉诺塔问题来自一个古老的传说:在世界刚被创建的时候有一座钻石宝塔(塔A),其上有64个金碟。所有碟子按从大到小的次序从塔底堆放至塔顶。紧挨着这座塔有另外两个钻石宝塔(塔B和塔C)。从世界创始之日起,婆罗门的牧师们就一直在试图把塔A上的碟子移动到塔C上去,其间借助于塔B的帮助。每次只能移动一个碟子,任何时候都不能把一个碟子放在比它小的碟子上面。当牧师们完成任务时,世界末日也就到了。
采用问题归约法把汉诺塔问题分成以下三个步骤实现:
- 将塔A上的n-1个碟子借助塔C先移到塔B上
- 把塔A上剩下的一个碟子移到塔C上
- 把n-1个碟子从塔B借助于塔A移到塔C上
🧡🧡实现🧡🧡
def move(start, end):
# print(f'{start}->{end}')
global cnt
cnt += 1
def dfs(n, a, b, c): # 把 n个a 通过b 移动到c
if n == 1:
move(a, c)
else:
dfs(n - 1, a, c, b)
move(a, c)
dfs(n - 1, b, a, c)
import time
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 为了支持中文字体
if __name__ == "__main__":
n=list(range(15,23)) # 盘子数
t=[] # 运行时间
m=[] # 递归调用次数
start=time.time()
for num in n:
print(f'======{num}个盘子======')
start_time=time.time()
cnt=0
dfs(num,'a','b','c')
end_time=time.time()
run_time=round(end_time-start_time,5)
print(f'运行时间:{run_time}秒, 递归调用次数:{cnt}\n')
t.append(run_time)
m.append(cnt)
end=time.time()
print("总共运行时间:%s 秒"%(end-start))
# 创建两幅子图
fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1, sharex=True)
# 在第一幅子图中绘制折线图
ax1.plot(n, t)
ax1.set_title("运行时间t随盘子数n的变化")
ax1.set_ylabel("运行时间t /s")
# 在第二幅子图中绘制折线图
ax2.plot(n, m)
ax2.set_title("递归调用次数m随盘子数n的变化")
ax2.set_xlabel("盘子数n")
ax2.set_ylabel("递归调用次数m /次")
# 调整子图间距
plt.subplots_adjust(hspace=0.5)
# 显示图形
plt.show()
🧡🧡总结🧡🧡
1.让盘子数从2?开始到7进行实验,记录程序运行时间和递归调用次数。
递归调用次数在程序中可以用move()函数调用次数来表示,也即代表移动盘子的次数。
当盘子数从2到7时,由于运行时间都极短(如下图),不方便看出时间上的差距,因此我将盘子数设定为15到22,方便看出关系。
2.画出盘子数n和运行时间t?、递归调用次数m的关系图,并进行分析
可见:随着盘子数的增加,运行时间和递归调用次数也会增加,且都呈指数增长的趋势。其中递归调用次数(移动盘子的次数)为与盘子数的关系为2n-1。同样地,运行时间复杂度为O(2n),随着n次数的增大,求得最终解的时间将恐怖增长。