遞迴函式最噁心的時候莫非棧溢位(stack overflow)。
ps:另外很多人在學習python的過程中,往往因為沒有好的教程或者沒人指導從而導致自己容易放棄,為此我建了個python交流.裙 :一久武其而而流一思(數字的諧音)轉換下可以找到了,裡面有最新python教程專案可拿,不懂的問題多跟裡面的人交流,都會解決哦!
如何解決?
人為設定遞迴深度
使用python寫的遞迴程式如果遞迴太深, 那麼極有可能因為超過系統預設的遞迴深度限制而出現錯誤。一般預設遞迴長度在1000左右。
runtimeerror: maximum recursion depth exceeded in comparison
顯然此時我們可以人為修改
import sys
sys.setrecursionlimit(1000000) #括號中的值為遞迴深度
尾遞迴優化
注:這只是一種思維的科普
解決遞迴呼叫棧溢位的另一種方法是通過尾遞迴優化,事實上尾遞迴和迴圈的效果是一樣的,所以,把迴圈看成是一種特殊的尾遞迴函式也是可以的。
尾遞迴是指,在函式返回的時候,呼叫自身本身,並且,return語句不能包含表示式。這樣,編譯器或者直譯器就可以把尾遞迴做優化,使遞迴本身無論呼叫多少次,都只占用乙個棧幀,不會出現棧溢位的情況。
上面的fact(n)函式由於return n * fact(n - 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)
可以看到,return fact_iter(num - 1, num * product) 僅返回遞迴函式本身,num - 1和num * product在函式呼叫前就會被計算,不影響函式呼叫。
fact(5)對應的fact_iter(5, 1)的呼叫如下:
===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120
尾遞迴呼叫時,如果做了優化,棧不會增長,因此,無論多少次呼叫也不會導致棧溢位。
遺憾的是,大多數程式語言沒有針對尾遞迴做優化,python直譯器也沒有做優化,所以,即使把上面的fact(n)函式改成尾遞迴方式,也會導致棧溢位。
python標準的直譯器沒有針對尾遞迴做優化,任何遞迴函式都存在棧溢位的問題
棧溢位和棧記憶體溢位
棧記憶體溢位是指使用者棧的大小最多為8 10mb,分配超過棧大小的變數則會導致棧記憶體溢位。如char c 1024102411 11mb 棧溢位指的是程式向棧中某個變數中寫入的位元組數超過了這個變數本身所申請的位元組數,因而導致與其相鄰的棧中的變數的值被改變。如char c 10 memset c...
棧溢位,記憶體溢位
對於一台伺服器而言,每乙個使用者請求,都會產生乙個執行緒來處理這個請求,每乙個執行緒對應著乙個棧,棧會分配記憶體,此時如果請求過多,這時候記憶體不夠了,就會發生棧記憶體溢位。棧溢位是指不斷的呼叫方法,不斷的壓棧,最終超出了棧允許的棧深度,就會發生棧溢位,比如遞迴操作沒有終止,死迴圈。可以把記憶體比作...
棧溢位攻擊c語言 棧溢位攻擊
我們先來看下面的乙個例子 include int main gets str printf str s n str return 0 在 main 函式內部定義乙個字元陣列,並通過 gets 為它賦值。在vs2010 debug模式下執行程式,當輸入的字元不超過10個時,可以正確輸出,但是當輸入的字...