在函数内部,可以调用其他函数,如果调用了函数本身,这个函数就是递归函数。
计算阶乘
fact(n) = n! = 1 x 2 x 3 x ... x n = fact(n-1) x n
def fact(n):
if n == 1:
return 1
return n * fact(n-1)
使用递归函数需要防止栈溢出。在计算机中,函数的调用是通过栈(stack)这种数据结构实现的,每当进入一个函数,栈就会加一层栈帧,每当函数返回,就会减一层栈帧。由于栈的大小是有限的,因此,递归调用次数过多会导致栈溢出。解决栈溢出的方法是尾递归优化。实际上,尾递归和循环的效果是一样的。尾递归是指函数在返回的时候,调用自身本身,且return
语句不能包含表达式。这样,编译器或解释器可以把尾递归做优化,使得递归不论调用多少次,都只是用一个栈帧,不会出现栈溢出的情况。上面的函数中使用了return n * fact(n-1)
,引用了乘法表达式,故不是尾递归,应改为:
def fact(n):
return fact_iter(n,1)
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
遗憾的是,大多是编程语言设计都没有针对尾递归做优化,Python解释器也没有,因此即使这样改了也会出现栈溢出的问题。
汉诺塔问题
汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
<img src="https://ws1.sinaimg.cn/large/006tNc79gy1foonyqnegtj30go0gojsb.jpg"
style="zoom:50%" />
编写函数move(n, x, y, z)
,接收参数n
,n
表示三个柱子A、B、C中第一个柱子A的盘子数量,然后打印出盘子从A借助B移动到C的方法:
# ~/usr/python_test/hannuota.py
def move(n, x, y, z):
if n == 1:
print(x, '-->', z)
return
else:
move(n-1, x, z, y)
move(1, x, y, z)
move(n-1, y, x, z)
return
move(4, 'A', 'B', 'C')
lijing@lijingdeMacBook-Pro > python3 ~/python_test/hannuota.py
A --> B
A --> C
B --> C
A --> B
C --> A
C --> B
A --> B
A --> C
B --> C
B --> A
C --> A
B --> C
A --> B
A --> C
B --> C
网友评论