解释这个河内塔解决方案(用python编写)

时间:2017-07-10 13:27:14

标签: python-3.x recursion towers-of-hanoi

我试图了解这个特殊的河内塔问题解决方案。这是一种递归解决方案。

A = [5, 4, 3, 2, 1]
B = []
C = []

def move(n, source, target, auxiliary, frm):
print('n here : ', n)
if n > 0:
    print('frm : ', frm, n)

    # move n - 1 disks from source to auxiliary, so they are out of the way
    move(n - 1, source, auxiliary, target, '1')

    # move the nth disk from source to target
    target.append(source.pop())

    print('n:', n)

    # Display our progress
    print(source, target, auxiliary, '##############', sep = '\n')

    # move the n - 1 disks that we left on auxiliary onto target
    move(n - 1, auxiliary, target, source, '2')

# initiate call from source A to target C with auxiliary B
move(5, A, C, B, '0')

你可以在这里看到它 https://repl.it/JUzY/0

在#########的第一行打印后,n = 0的值变为2.这是怎么发生的?我错过了一些递归的概念吗?帮助我理解这一点。

1 个答案:

答案 0 :(得分:2)

您需要了解范围。你说" n = 0的值,但突然变成2"。但是有多个" n",因为n的范围是方法。每次调用move时,该方法调用都有自己的n实例,并带有自己的值。

在脚本底部,您拨打move(5, A, C, B, '0')。在通话中,n == 5。首先要做的是致电move(n - 1, source, auxiliary, target, '1')。在调用n == 4。但是当它最终返回时,原始函数调用中的n仍为5

理解递归如何工作的一个好方法是使用纸张手工完成程序执行。我将使用便利贴进行功能调用,并使用笔记本进行输出。您还可以使用列表A,B,C的形式获得"模型"。由于元素四处移动,您可以用纸屑或拼字游戏瓷砖表示这些元素。

从初始move(4, A, C, B, '0')开始。 (我从4开始,因为步骤数量迅速增长)。因为我们正在调用一个函数,所以使用一个新的post-it来表示函数调用,然后在其上写:

n = 4
source = A
target = C
auxiliary = B
frm = '0'

现在通过代码move来完成它所说的内容。在此页面上写下任何print输出。

当您接到move(n - 1, source, auxiliary, target, '1')的来电时,请记下您所在行号的贴子,然后获取新的帖子,并写下要进入的值它:

n = 4     (because 4 - 1)
source = A
target = B  (caller's auxiliary)
auxiliary = C (caller's target)
frm = '1'

将此放在之前的帖子上。在重新显示之前,您不得查看被遮盖的帖子。继续这样做,你最终会得到一个五个叠加后。

第五个帖子有n = 0。现在,当您单步执行代码时,因为它以if n > 0:开头,所以调用立即返回。这个函数调用结束了,所以撕掉一个post-it并把它扔掉。 post-it n等的值对其他帖子没有影响。

您记下了您所在的行号,因此请从该行手动执行代码。您将产生一些输出,然后再次调用move - 使用post-it进行此移动,再次覆盖当前的post-it并增加堆栈直到您使用n == 0进行调用并且可以从堆栈中删除一个项目。

如果你继续这样做,你会看到堆栈增长和缩小几次,你会到达原始的帖子一次,进行打印,然后再次增长堆栈,然后达到原始状态再次发布,最后完成。

便利贴的堆栈是程序运行时的执行堆栈的精确模型。当您看到堆栈跟踪(甚至是非递归程序)时,这就是它们所代表的内容。每个post-it是堆栈帧,并且局部变量的范围仅限于该堆栈帧。

河内的递归解决方案可以用英语表示:

  • 要移动大小为零的堆栈,请不要执行任何操作
  • 通过辅助将更大的堆栈从src移动到目标:
    • 首先将n-1个光盘从src移动到辅助光盘(让它们不受影响)
      • 就像我们这样做一样。
    • 然后将显示的光盘移至目的地
    • 然后使用src作为备用挂钩将n-1个光盘从辅助光盘移动到目的地
      • 就像我们这样做一样