递归函数 - 汉诺塔的魅力
- 在 Python函数内部,我们可以去调用其他函数。所以如果一个函数在内部调用自身,这个函数我们就称为递归函数。所以我们将以汉诺塔的例子来感受递归函数的方法与应用。
- 汉诺塔问题源于印度一个古老传说。相传大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上并规定,任何时候,在小圆盘上都不能放大圆盘,且在三根柱子之间一次只能移动一个圆盘。如下图1所示,请问应该如何操作?
主要通过对汉诺塔问题的探讨,让学习者了解并掌握递归函数的相关知识。
相关知识
- 在编程语言中,如果一种计算过程的其中每一步都会用到前一步或前几步的结果,这个计算过程就可以称为递归的。而用递归计算过程定义的函数,则被称为递归函数。递归函数的应用很广泛,例如连加、连乘及阶乘等问题都可以利用递归思想来解决。而汉诺塔问题也是递归函数的经典应用。
- 汉诺塔问题的解决思路是:如果我们要思考每一步怎么移可能会非常复杂,但是可以将问题简化。我们可以先假设除a柱最下面的盘子之外,已经成功地将a柱上面的63个盘子移到了b柱,这时我们只要再将最下面的盘子由a柱移动到c柱即可
如下图2所示:
- 当我们将最大的盘子由a柱移到c柱后,b柱上便是余下的63个盘子,a柱为空。因此现在的目标就变成了将这63个盘子由b柱移到c柱。这个问题和原来的问题完全一样,只是由a柱换为了b柱,规模由64变为了63。因此可以采用相同的方法,先将上面的62个盘子由b柱移到a柱,再将最下面的盘子移到c柱。
- 以此类推,再以b柱为辅助,将a柱上面的62个圆盘最上面的61个圆盘移动到b柱,并将最后一块圆盘移到c柱。我们已经发现规律,我们每次都是以a或b中一根柱子为辅助,然后先将除了最下面的圆盘之外的其他圆盘移动到辅助柱子上,再将最底下的圆盘移到c柱子上,不断重复此过程。
这个反复移动圆盘的过程就是递归。例如我们每次想解决n个圆盘的移动问题,就要先解决(n-1)个盘子进行同样操作的问题。我们先假设a柱上只有3个圆盘,
利用Python 进行编程实现圆盘的移动
代码如下:
def move(n, a, b, c):
if(n == 1):
print(a,"->",c)
return
move(n-1, a, c, b)
move(1, a, b, c)
move(n-1, b, a, c)
move(3, "a", "b", "c")
函数运行结果:
a -> c
a -> b
c -> b
a -> c
b -> a
b -> c
a -> c
程序分析:
- 首先我们定义了一个函数move(n,a,b,c),参数n代表a柱上的圆盘个数,a,b,c三个柱子的顺序代表要将a柱上的圆盘最终移动到c柱上,然后b柱作为中间柱。
def move(n, a, b, c):
- 我们在递归函数中肯定会有终止递归的条件。第2到4行的代码就是表示,当a柱上的圆盘个数为1时,就中止递归并返回。因为此时a柱上面只有一个圆盘,肯定就是直接把圆盘从a柱移动到c柱了。
if(n == 1):
print(a,"->",c)
return
- 第5行的代码move(n-1, a, c, b)表示,先得把a柱上的n-1个圆盘从a柱移动到b柱,这时c柱是中间辅助柱。
move(n-1, a, c, b)
- 第6行的代码move(1, a, b, c)表示,当条件n=1的时候,把a柱上剩下的1个最大圆盘从a柱移动到c柱。
move(1, a, b, c)
- 第7行的代码move(n-1, b, a, c)表示,现在n-1个圆盘已经转移到b柱上了,还是递归调用move函数,将n-1个圆盘从b柱移动到c柱,这时a柱是中间辅助柱。
move(n-1, b, a, c)
最后我们调用move函数将3个圆盘从a柱移动到到c柱。当移动64个圆盘时,只需要将调用函数move(n,a,b,c)中的n变为64即可。这个计算量是十分巨大的,也只能交给计算机去解决。
move(3, "a", "b", "c")
小结
- 我们通过汉诺塔的例子感受了递归函数的基本思路,并尝试解决了一个具体问题。递归函数的优点是定义清晰、思路简洁,能够极大简化编程过程。理论上,所有的递归函数都可以用循环的方法代替,但循环方法的编程过程要比递归函数复杂很多。
编程要求
- 实现相应的功能。具体要求如下:
- 定义一个函数fact(n),实现的功能是对输入的正整数n进行n!运算;
- 调用函数fact(n),对输入的正整数n进行阶乘运算,并输出计算结果。
测试说明
测试输入:
5
预期输出:
120
测试输入:
6
预期输出:
720
测试输入:
7
预期输出:
5040
测试输入:
8
预期输出:
40320
上代码: