关于递归:解释此河内塔的解决方案(用python编写)

Explain this Towers of Hanoi solution (written in python)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 = '\
')

    # 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。这是怎么发生的?我是否错过了一些递归概念?帮助我理解这一点。


您需要了解范围。您说" 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表示该函数调用,并在其上写上:

1
2
3
4
5
n = 4
source = A
target = C
auxiliary = B
frm = '0'

现在按照move的代码进行操作,如其所说。在此输出上写任何print输出。

当您到达move(n - 1, source, auxiliary, target, '1')的呼叫时,在您所在的行号的便笺上做一个记录,然后获得一个新的便笺,并写上要输入的值:

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

将其放在上一个帖子的顶部。在重新显示之前,不允许您查看掩盖的便利贴。继续这样做,最终将得到一叠五个便笺纸。

第五个便利贴具有n = 0。现在,当您逐步浏览其代码时(因为它以if n > 0:开头),该调用将立即返回。此函数调用结束,因此将其拆下并扔掉。该便利贴中的n等的值对其他便利贴没有影响。

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

如果继续这样做,您会看到纸叠多次收缩和收缩,您将到达原始纸浆一次,进行打印,然后再次增大纸叠,然后再次到达原始纸浆,最后完成。

便利贴堆栈是程序运行时执行堆栈的精确模型。当您看到堆栈跟踪(甚至是非递归程序)时,它们就是它们的代表。每个便利贴都是一个堆栈框架,局部变量的作用域仅限于该堆栈框架。

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

  • 要移动大小为零的堆栈,请勿执行任何操作
  • 要通过辅助将较大的堆栈从src移动到目标,请执行以下操作:

    • 首先将n-1个光盘从src移到辅助光盘(以使其摆脱干扰)

      • 以与我们执行此操作相同的方式进行操作。
    • 然后将显示的光盘移到目标位置
    • 然后使用src作为备用钉将n-1个光盘从辅助光盘移动到目标光盘

      • 以与我们执行此操作相同的方式进行操作