遞迴和尾遞迴優化
遞迴
遞迴簡而言之就是自己呼叫自己。使用遞迴解決問題的核心就是分析出遞迴的模型,看這個問題能拆分出和自己類似的問題並且有一個遞迴出口。比如最簡單的就5的階乘,可以把它拆分成5*4!,然後求4!又可以呼叫自己,這種問題顯然可以用遞迴解決,遞迴的出口就是求1!,可以直接返回1。用Python實現如下:
def fact(n):
if n==1:
return n
return n*fact(n - 1);
print(fact(5))
遞迴出口(終止遞迴的條件)
1.滿足一定的層數
2.滿足某一臨界值
在使用遞迴函式需要注意防止棧溢位。在計算機中,函式呼叫是通過棧(stack)這種資料結構實現的,每當進入一個函式呼叫,棧就會加一層棧幀,每當函式返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞迴呼叫的次數過多,會導致棧溢位。
python預設的最大遞迴層數:998,
允許的最大遞迴層數是3925-3929之間浮動,這個是和計算機有關係的,不然也不會是一個浮動的數字了(數學邏輯講求嚴謹)。
詳解點選進入
尾遞迴
尾遞迴就是遞迴語句在函式最後執行,且無需對返回值進行進一步操作。
(換句話說:在函式返回的時候,呼叫自身本身,並且,return語句不能包含表示式。)
編譯器會對這種遞迴進行優化,在進入深層遞迴時候,不是在遞迴棧進行入棧操作,而是直接覆蓋棧頂。
線性遞迴與尾遞迴區別如下:
線性遞迴:
long Rescuvie(long n) { return (n == 1) ? 1 : n * Rescuvie(n - 1); }
尾遞迴:
long TailRescuvie(long n, long a) {
return (n == 1) ? a : TailRescuvie(n - 1, a * n);
}
long TailRescuvie(long n) {//封裝用的
return (n == 0) ? 1 : TailRescuvie(n, 1);
}
當n = 5時
對於線性遞迴, 他的遞迴過程如下:
Rescuvie(5) {5 * Rescuvie(4)} {5 * {4 * Rescuvie(3)}} {5 * {4 * {3 * Rescuvie(2)}}} {5 * {4 * {3 * {2 * Rescuvie(1)}}}} {5 * {4 * {3 * {2 * 1}}}} {5 * {4 * {3 * 2}}} {5 * {4 * 6}} {5 * 24} 120
對於尾遞迴, 他的遞迴過程如下:
TailRescuvie(5)
TailRescuvie(5, 1)
TailRescuvie(4, 5)
TailRescuvie(3, 20)
TailRescuvie(2, 60)
TailRescuvie(1, 120)
120
很容易看出, 普通的線性遞迴比尾遞迴更加消耗資源, 在實現上說, 每次重複的過程
呼叫都使得呼叫鏈條不斷加長. 系統不得不使用棧進行資料儲存和恢復.而尾遞迴就
不存在這樣的問題, 因為他的狀態完全由n和a儲存.
總的來說,尾遞迴在普通尾呼叫的基礎上,多出了2個特徵:
1.在尾部呼叫的是函式自身 (Self-called);
2.可通過優化,使得計算僅佔用常量棧空間 (Stack Space)。
尾遞迴優化
尾遞迴優化是解決遞迴呼叫棧溢位的方法,、
優化方式:藉助編譯器或執行環境提供的現成的尾遞迴優化特性.
大多數程式語言沒有針對尾遞迴做優化,Python直譯器也沒有做優化
也就是 pyhton 中沒有尾遞迴優化。