本周笔记迭代、递归和树递归
iteration
Fibonacci数列
首项为0,第一项为1,接下来每项的值等于其前两项之和。
定义一个函数,返回Fibonacci数列第n项的值:
def fib(n):
"""computer the nth Fibonacci number.
>>> fib(0)
0
>>> fib(8)
21
"""
k, kth, diff = 0, 0, 1
while k < n:
kth, diff = kth + diff, kth
k = k + 1
return kth
返回值
逆序打印一个给定整数每个位数上的数,直到找到指定的数
def end(n, d):
"""
>>> end(45678, 4)
8
7
5
4
"""
while n > 0:
last, n = n % 10, n // 10
print(last)
if d == last:
return None
end(45678, 5)
自调用
使用函数print_sums(1)(3)(5)时,先执行print_sums(1),返回值是其内部定义的一个函数,此时函数没有执行。当执行rint_sums(1)(3),执行的函数是next_sum(3), 返回的是print_sums(3+1)的结果:函数print_sums。此时局部环境中n=4,执行print_sums(1)(3)(5)就是执行next_sum(5), 返回的是print_sums(4+5)的结果:函数print_sums。
recurrsion
例子:整数各位上的数之和
一个整数能被9整除,那么该整数各位上的数之和能被9整除,如2016被9整除,2+0+1+6 = 9能被9整除。可以将2016拆分两部分201各位上的和与6的和。201又能拆分为20、1.
递归调用结束条件满足时,即base case满足时,递归结束。base case的return在整个递归执行过程之只执行一次。
online python tutor 调试
递归环境框图
递归调用时的框图:
每次递归调用都不会马上返回当前环境的最终结果,只有当递归终结条件满足时,从最后的调用开始,一次向上返回结果。
迭代和循环
迭代是递归的一种特殊形式。
使用while迭代式需要多个变量追踪数据的变化;而是用递归需要开辟更多的内存空间去追踪每个局部环境的变量。
如何保证递归正确执行
这里计算阶乘,类似数学归纳法。
递归调用中,一定要保证递归终止条件是能够被满足的。
将递归和迭代相互转化
递归调用顺序
实现一个堆叠功能
输入12345
输出:
12345
1234
123
12
1
12
123
1234
12345
递归调用中,当不满足递归停止条件时,打印输入参数n,停止调用之前,函数cascade之后的print都不会执行。递归调用第二次时,及执行到上图框图f3时,cascade函数才被真正执行完一次,返回None,然后回到框图f2执行下一句print(n),此时f2执行完毕,回到f1,执行下一句print(n)。
堆叠输出的两种定义方式
反向堆叠
输入:1234
输出:
通过一个lambda函数递归调用grow和shrink函数。
在线调试:online python tutor
Tree recurrsion
使用递归实现Fibonacci数列
这种实现方式有一个问题,那就是n以前的项总是被重复计算。计算fib(3)时需要计算fib(2),fib(1),计算fib(2)又要计算fib(1), fib(0)。每次计算fib(m), 比他更小的项总是重新计算。
使用trace追踪递归调用过程
# Decorators
def mytrace(fn):
"""Return a function equivalent to fn that also prints trace output.
fn -- a function of one argument.
"""
def traced(x):
print('Calling', fn, 'on argument', x)
return fn(x)
return traced
@mytrace
def fib(n):
"""Compute the nth Fibonacci number.
>>> fib(8)
21
"""
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-2) + fib(n-1)
fib(5)
上图打印的递归调用过程和ppt上的树节点的结算顺序一致。
计算一个正整数的所有可能正加数组合的个数。
比如计算和为6的加数中,小于等于4的所有加数组合数。
分为两种情况考虑,包括给定数的情况和不包括给定数的情况。在count_partitions(6,4)这个例子中,第一个count_partitions(n-m, m)中n-m是计算余数6-4=2是否还可分,就如树一样,从顶往下搜索。递归结束条件if n==0 和 if n<0返回是否存在。转为代码:
使用trace追踪递归过程:
def mytrace(fn):
"""Return a function equivalent to fn that also prints trace output.
fn -- a function of one argument.
"""
def traced(x, y):
print('Calling', fn, 'on argument', x, y)
return fn(x, y)
return traced
@mytrace
def count_partitions(n, m):
"""Count the partitions of n using parts up to size m.
>>> count_partitions(6, 4)
9
>>> count_partitions(10, 10)
42
"""
if n == 0:
return 1
elif n < 0:
return 0
elif m == 0:
return 0
else:
with_m = count_partitions(n-m, m)
without_m = count_partitions(n, m-1)
return with_m + without_m
count_partitions(4, 4)
在线调试,调试结果:
参考:
https://www.bilibili.com/video/BV16W411W76H?p=46
https://inst.eecs.berkeley.edu/~cs61a/sp18/
http://composingprograms.com/pages/17-recursive-functions.html