題目:
計算階乘n!=n*(n-1)*(n-2)*…3*2*1
用遞歸函數(shù)來表示為:
def f(x):
? ?if x==1:
? ? ? ?return 1
? ?return x*f(x-1)
計算5的階乘5!,運行正確。
接著計算大一點的數(shù)1000?。?br>
可以看到運行結(jié)果報錯了,這是因為出現(xiàn)了棧溢出。
在計算機中,函數(shù)調(diào)用是通過棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的,每當進入一個函數(shù)調(diào)用,棧就會加一層棧幀,每當函數(shù)返回,棧就會減一層棧幀。由于棧的大小不是無限的,所以,遞歸調(diào)用的次數(shù)過多,會導致棧溢出。
在計算1000!時,遞歸函數(shù)使得函數(shù)調(diào)用次數(shù)非常多,層級很深,返回卻很少,導致堆棧無法容納大量的調(diào)用返回地址,從而產(chǎn)生溢出。
解決思路是:
對return語句進行尾遞歸優(yōu)化,也就是避免在return語句里出現(xiàn)表達式(上文代碼中的‘return x*f(x-1)就是一個乘法表達式’),這樣解釋器或編譯器就會自動把尾遞歸進行優(yōu)化,使得無論遞歸多少次函數(shù),都只占用一個棧,從而避免溢出的產(chǎn)生(原理上)。
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)
上文代碼是做了尾遞歸優(yōu)化的情形,可惜的是python并不支持尾遞歸優(yōu)化,python遞歸的次數(shù)在1000以內(nèi)不會產(chǎn)生溢出,1000以上就要考慮用循環(huán)來代替了。
可修改成:
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)
print function(1000)已經(jīng)可以正常輸出了。
案例及學習材料來源:廖雪峰大大的博客