天天看點

python 遞歸函數 棧溢出

題目:

計算階乘n!=n*(n-1)*(n-2)*…3*2*1

用遞歸函數來表示為:

def f(x):

   if x==1:

       return 1

   return x*f(x-1)

python 遞歸函數 棧溢出

代碼截圖

python 遞歸函數 棧溢出

運作結果

計算5的階乘5!,運作正确。

接着計算大一點的數1000!:

python 遞歸函數 棧溢出

代碼截圖

python 遞歸函數 棧溢出

運作結果

python 遞歸函數 棧溢出

運作結果

可以看到運作結果報錯了,這是因為出現了棧溢出。

在計算機中,函數調用是通過棧(stack)這種資料結構實作的,每當進入一個函數調用,棧就會加一層棧幀,每當函數傳回,棧就會減一層棧幀。由于棧的大小不是無限的,是以,遞歸調用的次數過多,會導緻棧溢出。

在計算1000!時,遞歸函數使得函數調用次數非常多,層級很深,傳回卻很少,導緻堆棧無法容納大量的調用傳回位址,進而産生溢出。

解決思路是:

對return語句進行尾遞歸優化,也就是避免在return語句裡出現表達式(上文代碼中的‘return x*f(x-1)就是一個乘法表達式’),這樣解釋器或編譯器就會自動把尾遞歸進行優化,使得無論遞歸多少次函數,都隻占用一個棧,進而避免溢出的産生(原理上)。

def fact(n):

    return fact_iter(n, 1)

def fact_iter(num, product):

    if num == 1:

        return product

    return fact_iter(num - 1, num * product)

print fact(5)

上文代碼是做了尾遞歸優化的情形,可惜的是python并不支援尾遞歸優化,python遞歸的次數在1000以内不會産生溢出,1000以上就要考慮用循環來代替了。

可修改成:

def function(x):

    s = 1

    if x == 1:

        return 1

    if x>1:

        for i in range(1,x+1):

            s = s*i

         return s

    else:

        return 'No Answer!'

print function(5)

python 遞歸函數 棧溢出

代碼截圖

python 遞歸函數 棧溢出

運作結果

print function(1000)已經可以正常輸出了。