cs61a 第三週課時筆記-令人驚訝的遞歸實現!

本週筆記迭代、遞歸和樹遞歸

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)

自調用

3-1
使用函數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.
3-2
遞歸調用結束條件滿足時,即base case滿足時,遞歸結束。base case的return在整個遞歸執行過程之只執行一次。
online python tutor 調試
3-3

遞歸環境框圖

遞歸調用時的框圖:
3-4
每次遞歸調用都不會馬上返回當前環境的最終結果,只有當遞歸終結條件滿足時,從最後的調用開始,一次向上返回結果。

迭代和循環

3-5
迭代是遞歸的一種特殊形式。
使用while迭代式需要多個變量追蹤數據的變化;而是用遞歸需要開闢更多的內存空間去追蹤每個局部環境的變量。

如何保證遞歸正確執行

3-6
這裏計算階乘,類似數學歸納法。
遞歸調用中,一定要保證遞歸終止條件是能夠被滿足的。

將遞歸和迭代相互轉化

3-8

3-7

遞歸調用順序

實現一個堆疊功能

輸入12345
輸出:
12345
1234
123
12
1
12
123
1234
12345
3-8
遞歸調用中,當不滿足遞歸停止條件時,打印輸入參數n,停止調用之前,函數cascade之後的print都不會執行。遞歸調用第二次時,及執行到上圖框圖f3時,cascade函數才被真正執行完一次,返回None,然後回到框圖f2執行下一句print(n),此時f2執行完畢,回到f1,執行下一句print(n)。

堆疊輸出的兩種定義方式

3-9

反向堆疊

輸入:1234
輸出:
3-10
通過一個lambda函數遞歸調用grow和shrink函數。

在線調試:online python tutor

Tree recurrsion

使用遞歸實現Fibonacci數列

3-11
這種實現方式有一個問題,那就是n以前的項總是被重複計算。計算fib(3)時需要計算fib(2),fib(1),計算fib(2)又要計算fib(1), fib(0)。每次計算fib(m), 比他更小的項總是重新計算。
3-12

使用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)

3-13
上圖打印的遞歸調用過程和ppt上的樹節點的結算順序一致。

計算一個正整數的所有可能正加數組合的個數。

比如計算和爲6的加數中,小於等於4的所有加數組合數。
3-14
分爲兩種情況考慮,包括給定數的情況和不包括給定數的情況。在count_partitions(6,4)這個例子中,第一個count_partitions(n-m, m)中n-m是計算餘數6-4=2是否還可分,就如樹一樣,從頂往下搜索。遞歸結束條件if n==0 和 if n<0返回是否存在。轉爲代碼:
3-15
使用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)

在線調試,調試結果:
3-14

參考:
https://www.bilibili.com/video/BV16W411W76H?p=46
https://inst.eecs.berkeley.edu/~cs61a/sp18/
http://composingprograms.com/pages/17-recursive-functions.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章