1. 程式人生 > >遞迴和尾遞迴優化

遞迴和尾遞迴優化

遞迴

遞迴簡而言之就是自己呼叫自己。使用遞迴解決問題的核心就是分析出遞迴的模型,看這個問題能拆分出和自己類似的問題並且有一個遞迴出口。比如最簡單的就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 中沒有尾遞迴優化。