汉诺塔(Tower of Hanoi)

Admin
发布于 2026-05-21 / 2 阅读
0
0

这段代码实现了经典的**汉诺塔(Tower of Hanoi)**问题,并输出了将 $n$ 层圆盘从起始柱移动到目标柱的每一步操作。

下面我将从实现原理、用途和注意事项三个方面为您详细解释:

### 1. 实现原理

汉诺塔问题的核心在于**分治法**和**递归**。问题的规则是:每次只能移动一个圆盘,且大盘不能压在小盘上面。

代码中的 move(n, a, b, c) 函数表示:将 $n$ 个圆盘从柱子 a(起始柱)借助柱子 b(中转柱)移动到柱子 c(目标柱)。

递归的拆解思路如下:

要将 $n$ 个圆盘从 A 移动到 C,可以分为三个步骤:

1. 把上面的 $n-1$ 个圆盘,从 A 借助 C 移动到 B。对应代码move(n-1, a, c, b)

2. 把最底下的最大圆盘(第 $n$ 个),从 A 直接移动到 C。对应代码move(1, a, b, c)

3. 把刚才移到 B 上的 $n-1$ 个圆盘,从 B 借助 A 移动到 C。对应代码move(n-1, b, a, c)

递归的终止条件(Base Case):

当 $n=1$ 时,说明只剩下一个圆盘,可以直接从 A 移动到 C,此时直接打印移动指令 print(a, '-->', c)

以代码中的 move(3, 'A', 'B', 'C') 为例,其执行过程的树状展开如下:

- move(3, A, B, C)

- move(2, A, C, B) -> 将上面2个盘从A移到B

- move(1, A, B, C) -> 打印 A --> C

- move(1, A, C, B) -> 打印 A --> B

- move(1, C, A, B) -> 打印 C --> B

- move(1, A, B, C) -> 将最底下的盘从A移到C,打印 A --> C

- move(2, B, A, C) -> 将B上的2个盘移到C

- move(1, B, C, A) -> 打印 B --> A

- move(1, B, A, C) -> 打印 B --> C

- move(1, A, B, C) -> 打印 A --> C

### 2. 用途

- 算法教学:汉诺塔是讲解“递归”和“分治思想”最经典的案例之一,能帮助初学者深刻理解函数调用自身、状态参数传递和递归终止条件的设置。

- 栈的模拟:汉诺塔的三个柱子本质上就是三个“栈”(后进先出,LIFO),该问题常用于演示栈的操作特性。

- 复杂度分析练习:通过汉诺塔可以直观地理解指数级时间复杂度 $O(2^n)$ 的增长爆炸效应。

### 3. 注意事项

- 时间复杂度极高:移动 $n$ 个圆盘所需的步数为 $2^n - 1$。这是一个指数级复杂度 $O(2^n)$,当 $n$ 稍微变大(如 $n=30$),步数就会超过10亿,程序运行时间会变得极长;若 $n=64$(传说中婆罗门寺庙的汉诺塔),即使计算机每秒运算亿次,也需要数百亿年才能输出完毕。因此,**不要传入过大的 $n$ 值**。

- 空间复杂度:由于递归的深度为 $n$,所以空间复杂度为 $O(n)$,这由 Python 的最大递归深度限制决定。如果 $n$ 超过了默认的递归深度(通常为1000),程序会抛出 RecursionError

- 参数顺序的意义move(n, a, b, c) 中的 b 是中转柱,但在递归调用中,它的角色是动态变化的。例如在 move(n-1, a, c, b) 中,原来的目标柱 c 变成了中转柱,原来的中转柱 b 变成了目标柱。理解参数位置的轮换是理解这段代码的关键。

- 代码的小优化:代码中的 move(1, a, b, c) 其实可以直接替换为 print(a, '-->', c),因为当 $n=1$ 时,中转柱 b 是不起作用的。修改后可以减少一次无意义的函数调用开销:

```python

def move(n, a, b, c):

if n == 1:

print(a, '-->', c)

else:

move(n-1, a, c, b)

print(a, '-->', c) # 替换 move(1, a, b, c)

move(n-1, b, a, c)

```

河内某寺庙有三根柱子、64个金盘,僧侣每天移动一个盘子,大盘不能压小盘,全部移完世界就毁灭。

实际64个盘子需要移动1844万亿次,永远移不完。


评论