Python遞歸函數

#1、計算階乘n! = 1 x 2 x 3 x ... x n,用函數fact(n)表示,可以看出:
#fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n
def fact(n):
    if n==1:
        return 1
    else:
        return(n * fact(n-1))
print(fact(5))
print(fact(6))


解決遞歸調用棧溢出的方法是通過尾遞歸優化,事實上尾遞歸和循環的效果是一樣的,所以,把循環看成是一種特殊的尾遞歸函數也是可以的。

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

上面的fact(n)函數由於return n * fact(n - 1)引入了乘法表達式,所以就不是尾遞歸了。要改成尾遞歸方式,需要多一點代碼,主要是要把每一步的乘積傳入到遞歸函數中:

def f(n):
    return f_iter(n,1)
def f_iter(n,tmpRes):
    if n==1:
        return tmpRes
    else:
        return f_iter(n-1,n*tmpRes)

print(fact(998))
print(f(997))  #大於這個數調用會棧溢出

尾遞歸調用時,如果做了優化,棧不會增長,因此,無論多少次調用也不會導致棧溢出。

遺憾的是,大多數編程語言沒有針對尾遞歸做優化,Python解釋器也沒有做優化,所以,即使把上面的fact(n)函數改成尾遞歸方式,也會導致棧溢出。

 

#2、漢諾塔的移動,請編寫move(n, a, b, c)函數,它接收參數n,表示3個柱子A、B、C中第1個柱子A的盤子數量,然後打印出把所有盤子從A藉助B移動到C的方法,

例如:

# 期待輸出:
# A --> C
# A --> B
# C --> B
# A --> C
# B --> A
# B --> C
# A --> C
#move(3, 'A', 'B', 'C')

分析:將n個盤子從a全部移向c,b作爲輔助柱子,此時是move(n,a,b,c)   --輔助柱子在變量中間

1)可以想象n-1個盤子從a移動到b,c作爲輔助柱子      move(n-1,a,c,b)

2)然後a可將柱子最下面那個盤子移動c,      a-->c

3)此時b有n-1個盤子,將n-1個盤子從b移動到c,a作爲輔助柱子,此時只是由原本的a移動到c變成了從b移動到c,重複之前的步驟,   move(n-1,b,a,c)

遞歸方法

def move(n,a,b,c):
    if n==1:
        print(a,' --> ',c)
        return 
    move(n-1,a,c,b)
    print(a,' --> ',c)
    move(n-1,b,a,c)
move(2, 'A', 'B', 'C')

move(3, 'A', 'B', 'C')

move(5, 'A', 'B', 'C')

A  -->  C
A  -->  B
C  -->  B
A  -->  C
B  -->  A
B  -->  C
A  -->  C
A  -->  B
C  -->  B
C  -->  A
B  -->  A
C  -->  B
A  -->  C
A  -->  B
C  -->  B
A  -->  C
B  -->  A
B  -->  C
A  -->  C
B  -->  A
C  -->  B
C  -->  A
B  -->  A
B  -->  C
A  -->  C
A  -->  B
C  -->  B
A  -->  C
B  -->  A
B  -->  C
A  -->  C

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