Python算法详解
上QQ阅读APP看书,第一时间看更新

3.2.3 实践演练——解决“汉诺塔”问题

为了说明递归算法的基本用法,接下来将通过一个具体实例的实现过程,详细讲解递归算法思想在编程中的基本应用。

1.问题描述

寺院里有3根柱子,第一根有64个盘子,从上往下盘子越来越大。方丈要求小和尚A1把这64个盘子全部移动到第3根柱子上。在移动的时候,始终只能小盘子压着大盘子,而且每次只能移动一个盘子。

方丈发布命令后,小和尚A1就马上开始了工作,下面看他的工作过程。

(1)聪明的小和尚A1在移动时,觉得很难,另外他也非常懒惰,所以找小和尚A2来帮他。他觉得要是A2能把前63个盘子先移动到第二根柱子上,自己再把最后一个盘子直接移动到第三根柱子上,再让A2把刚才的前63个盘子从第二根柱子上移动到第三根柱子上,整个任务就完成了。所以他找到另一个小和尚A2,然后下了如下命令。

① 把前63个盘子移动到第二根柱子上。

② 把第64个盘子移动到第三根柱子上。

③ 把前63个盘子移动到第三根柱子上。

(2)小和尚A2接到任务后,也觉得很难,所以他也和A1想的一样:要是有一个人能把前62个盘子先移动到第三根柱子上,再把最后一个盘子直接移动到第二根柱子上,再让那个人把刚才的前62个盘子从第三根柱子上移动到第二根柱子上,任务就算完成了。所以他找了另一个小和尚A3,然后下了如下命令。

① 把前62个盘子移动到第三根柱子上。

② 自己把第63个盘子移动到第二根柱子上。

③ 把前62个盘子移动到第二根柱子上。

(3)小和尚A3接到任务后,又把移动前61个盘子的任务“依葫芦画瓢”地交给了小和尚A4,这样一直递推下去,直到把任务交给第64个小和尚A64为止。

(4)此时此刻,任务马上就要完成了,只剩下小和尚A63A64要做的工作了。

小和尚A64移动第1个盘子,把它移开,然后小和尚A63移动给他分配的第2个盘子。

小和尚A64再把第1个盘子移动到第2个盘子上。到这里A64的任务完成,A63完成了A62交给他的任务的第一步。

2.算法分析

从上面小和尚的工作过程可以看出,只有A64的任务完成后,A63的任务才能完成,只有小和尚A2与小和尚A64的任务完成后,小和尚A1剩余的任务才能完成。只有小和尚A1剩余的任务完成后,才能完成方丈吩咐给他的任务。由此可见,整个过程是一个典型的递归问题。接下来我们以有3个盘子的情况来分析。

具体步骤如下。

(1)第1个小和尚命令第2个小和尚先把第一根柱子上的前2个盘子移动到第二根柱子上(借助第三根柱子)。

(2)第1个小和尚自己把第一根柱子上最后那个盘子移动到第三根柱子上。

(3)第1个小和尚命令第2个小和尚把前两个盘子从第二根柱子移动到第三根柱子上。

显然,第(2)步很容易实现。

其中第(1)步,第2个小和尚有两个盘子,继续执行以下步骤。

① 第3个小和尚把第一根柱子上的第1个盘子移动到第三根柱子上(借助第二根柱子)。

② 第2个小和尚自己把第一根柱子上的第2个盘子移动到第二根柱子上。

③ 第3个小和尚把第1个盘子从第三根柱子移动到第二根柱子上。

同样,上面的第②步很容易实现,但第3个小和尚只需要移动1个盘子,所以他不用再下派任务了(注意:这就是停止递归的条件,也叫边界值)。

第③步可以分解为,第2个小和尚还是有两个盘子,继续执行以下步骤。

(a)第3个小和尚把第二根柱子上的第1个盘子移动到第一根柱子上。

(b)第2个小和尚把第2个盘子从第二根柱子移动到第三根柱子上。

(c)第3个小和尚把第一根柱子上的盘子移动到第三根柱子上。

分析组合起来就是:1→3,1→2,3→2,借助第三根柱子移动到第二根柱子;1→3是懒人留给自己的活;2→1,2→3,1→3是借助别人帮忙,从第一根柱子移动到第三根柱子一共需要七步来完成。

如果是4个盘子,则第一个小和尚的命令中,第①步和第③步各有3个盘子,所以各需要7步,共14步,再加上第1个小和尚的第①步,所以4个盘子总共需要7+1+7=15步;同样,5个盘子需要15+1+15=31步,6个盘子需要31+1+31=63步……由此可以知道,移动n个盘子需要(2n−1)步。

假设用hannuo(n,a,b,c)表示把第一根柱子上的n个盘子借助第2根柱子移动到第3根柱子上。由此可以得出如下结论:第①步的操作是hannuo(n−1,1,3,2),第③步的操作是hannuo(n−1,2,1,3)。

3.具体实现

在下面的实例文件hannuo.py中,演示了使用递归算法解决“汉诺塔”问题的过程。

源码路径:daima\第3章\hannuo.py

i = 1
def move(n, mfrom, mto) :
  global i
  print("第%d步:将%d号盘子从%s -> %s" %(i, n, mfrom, mto))
  i += 1
def hanoi(n, A, B, C) :
  if n == 1 :
     move(1, A, C)
  else :
     hanoi(n - 1, A, C, B)
     move(n, A, C)
     hanoi(n - 1, B, A, C)
#********************程序入口**********************
try :
  n = int(input("please input a integer :"))
  print("移动步骤如下:")
  hanoi(n, 'A', 'B', 'C')
except ValueError:
  print("please input a integer n(n > 0)!" )

执行后会输出:

please input a integer :4
移动步骤如下:
第1步:将1号盘子从A -> B
第2步:将2号盘子从A -> C
第3步:将1号盘子从B -> C
第4步:将3号盘子从A -> B
第5步:将1号盘子从C -> A
第6步:将2号盘子从C -> B
第7步:将1号盘子从A -> B
第8步:将4号盘子从A -> C
第9步:将1号盘子从B -> C
第10步:将2号盘子从B -> A
第11步:将1号盘子从C -> A
第12步:将3号盘子从B -> C
第13步:将1号盘子从A -> B
第14步:将2号盘子从A -> C
第15步:将1号盘子从B -> C