1. 程式人生 > >python—遞歸函數

python—遞歸函數

aid 重新 技術 技術分享 block pri return語句 ack pow

遞歸函數

定義:即在函數定義中自己調用自己

  • 遞歸就是在過程或函數中自我調用
  • 遞歸必須有遞歸出口,即遞歸結束條件

舉個栗子—階乘:

def fact(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))
# 120

函數執行過程:

===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 *
fact(1)))) ===> 5 * (4 * (3 * (2 * 1))) ===> 5 * (4 * (3 * 2)) ===> 5 * (4 * 6) ===> 5 * 24 ===> 120

註意: 使用遞歸函數需要防止棧溢出。 函數調用是通過 棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會增加一層棧幀,每當函數返回,就會減一層棧幀。由於棧的空間是有限的,遞歸調用次數過多就會導致棧溢出


尾遞歸優化:

為了避免棧溢出,可以通過尾遞歸優化,尾遞歸實際與循環一樣,我們可以循環當做是特殊的尾遞歸.

尾遞歸是指,在函數返回的時候,調用自身本身,並且,return語句不能包含表達式。這樣,編譯器或者解釋器就可以把尾遞歸做優化,使遞歸本身無論調用多少次,都只占用一個棧幀,不會出現棧溢出的情況

# 尾遞歸的例子
def calc(n):
    print(n - 1)
    if n > -50:
        return calc(n-1)

根據尾遞歸定義,我們的階乘就不是尾遞歸了

def fact(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)

因為返回語句是一個乘法操作 ,return n * factorial(n - 1) 引入了乘法操作。

  • 尾遞歸調用時,如果做了優化,棧不會增長,因此,無論多少次調用也不會導致棧溢出 .
  • 使用遞歸函數的優點是邏輯簡單清晰,缺點是過深的調用會導致棧溢出。
  • 針對尾遞歸優化的語言可以通過尾遞歸防止棧溢出。尾遞歸事實上和循環是等價的,沒有循環語句的編程語言只能通過尾遞歸實現循環。
  • Python標準的解釋器沒有針對尾遞歸做優化,任何遞歸函數都存在棧溢出的問題。^_^

漢諾塔問題

漢諾塔:漢諾塔(又稱河內塔)問題是源於印度一個古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。

技術分享圖片

def move(n,a,b,c):
    if n == 1:
        print(‘move‘, a, ‘-->‘, c)
    else:
        move(n-1,a,c,b)
        move(1,a,b,c)
        move(n-1,b,a,c)

move(3,‘A‘,‘B‘,‘C‘)
# 打印結果:
# move A --> C
# move A --> B
# move C --> B
# move A --> C
# move B --> A
# move B --> C
# move A --> C

python—遞歸函數