简单来说,递归函数就是自己调用自己,递归要包含递归的所有基本情况(有时我们叫做递归出口和递归调用)我们从一个简单的例子开始:打印一个数字所有位之和,例如 123 = 1 + 2 + 3, 9684 = 9 + 6 + 8 + 4
def sum_digits(n):
if n < 10:
return n
else:
all_but_last, last = n // 10, n % 10
return sum_digits(all_but_last) + last
sum_digits(738)
问题被拆解为两个部分,加上前面的数和加上最后一位数,拆解后问题变得简单并且可以一直迭代继续拆解,具体的调用过程可以看一下我画的的environment diagram。
递归的剖析
递归函数的设计是为了简化原来的问题,而且有时候可以用其他设计方式解决。
例如计算一个数的阶乘
循环解决
def fact_iter(n):
total, k = 1, 1
while k <= n:
total, k = total * k, k + 1
return total
递归解决
def fact(n):
if n == 1:
return 1
else:
return n * fact(n-1)
那么递归函数的优势在哪里?
- 更加注重宏观上的设计,上面不考虑fact(n-1)是如何实现的
- 减少过程中变量的使用
- 注重问题的拆解,由繁到简
递归的互相嵌套
例如判断一个数是否为偶数,我们很容易得到一下思路
- 0为偶数
- 一个数为奇数如果这个数减一为偶数
- 一个数为偶数如果一个数减一为奇数
代码如下:
def is_even(n):
if n == 0:
return True
else:
return is_odd(n-1)
def is_odd(n):
if n == 0:
return False
else:
return is_even(n-1)
还可以表示为:
- 一个大于二的数与它减二的数奇偶性相同
def is_even(n):
if n == 0:
return True
elif n == 1:
return False
else:
return is_even(n - 2)
另外一个例子是一个二人游戏,一开始有n个石头,甲和乙轮流那石头,谁拿到最后一个石头就赢,规则如下:
- 甲每次拿一个
- 乙如果石头是偶数就那两个,奇数拿一个
- 甲先拿
设计一个函数,输入石头个数, 打印谁赢了
def play_jia(n):
if n == 0:
print("yi wins!")
else:
play_yi(n-1)
def play_yi(n):
if n == 0:
print("jia wins!")
elif is_even(n):
play_jia(n-2)
else:
play_jia(n-1)
递归序
递归对于新手来说最烦的就是它的调用过程,递归序我这里不展开,那么如何可视化函数的调用过程,我们可以通过打印处理看一下,例如从大到小然后从小到大打印数字的前缀
>>> def cascade(n):
if n < 10:
print(n)
else:
print(n)
cascade(n//10)
print(n)
>>> cascade(2013)
2013
201
20
2
20
201
2013
实战
给出一个整数n,上限m,n可以由 1 到 m的数相加组成,返回有多少情况
例如 n = 6, m = 4
- 6 = 2 + 4
- 6 = 1 + 1 + 4
- 6 = 3 + 3
- 6 = 1 + 2 + 3
- 6 = 1 + 1 + 1 + 3
- 6 = 2 + 2 + 2
- 6 = 1 + 1 + 2 + 2
- 6 = 1 + 1 + 1 + 1 + 2
- 6 = 1 + 1 + 1 + 1 + 1 + 1
一共有九种情况,返回9
我们首先分析一下情况,列出所以基本情况:
考虑四个情况
- n < 0 如果n搞着搞着发现小于0,那么这种情况不成立
- m == 0 搞着搞着发现上限为0,那么情况不成立
- n == 0 n 刚好减到0,一种情况
- 返回 f(n-m, m) + f(n, m-1) 重点分析第四种情况:所有都可分为两种情况,一种至少含有一个上限m,一种不含上限m,
def count_partitions(n, m):
if n == 0:
return 1
elif n < 0:
return 0
elif m == 0:
return 0
else:
return count_partitions(n - m, m) + count_partitions(n, m-1)
思考下句话是否正确:
任何递归函数都可以改为非递归函数
正确!我们可以手动实现函数压栈等过程
本文来自作者学习compsing programs的部分学习笔记