本週筆記迭代、遞歸和樹遞歸
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