遞迴實現原則

weixin_33935777發表於2018-09-25

級別:★☆☆☆☆
標籤:「遞迴」「四條遞迴實現基本原則」
作者: 陳彬
審校: QiShare團隊

引言:這篇文章主要講述遞迴實現原則。為了方便理解,每個原則都會對應的段程式碼例項。

一、定義:

目前我查到的資料中關於遞迴解釋有兩種:

  • 程式呼叫自身的程式設計技巧稱為遞迴。(源於知識豐厚的度娘)
  • 當一個函式用它自己來定義時就稱為是遞迴。(源於資料結構與演算法分析書籍)

第一種說法比較直接的描述了遞迴在程式的實現,具體實現見下面實現原則暫不贅述,而第二種說法偏向於演算法,利用數學知識更方便理解,如函式F(X)=2F(X-1)+X

二、四條實現基本原則:

1. 基準情形:

必須總有某些基準情形,它無需遞迴就能解除。如求斐波那契數,輸入1無需遞迴直接返回1,見圖1-1:

如若無基準情形傳入1後,由於非負數整數X-2操作會導致越界,產生超大數,該超大數還會繼續進行求斐波那契數操作,由於棧大小遠小於該超大數最終會發生棧溢位問題。假如棧足夠大能包容超大數量函式排程,求斐波那契數會從超大數再到0,形成迴圈。執行結果見圖1-2。

PS:在做demo時,發現一有趣問題,我定義求斐波那契數函式引數型別是無符號整形,我傳入了-1,結果編譯通過並且執行了,笑而不語。

2. 不斷推進:

對於那些需要遞迴求解的情形,每一次遞迴呼叫都必須要是求解狀況朝基準情形的方向推進。如使用函式F(X)=2*F(X-1)+X*X計算所需的值,當我輸入正整數5時,它的遞迴推進方向是F(5)->F(4)->F(3)->F(2)->F(1)->F(0),其中F(0)是基準情形,詳細見圖2-1。如果我傳入了-1,它的遞迴推進方向是F(-1)->F(-2)->F(-3)->F(-4)->F(-5)->F(-6)…->F(∞),離基準情形越來越遠最終會發生棧溢位問題。詳細見圖2-2。

3. 設計原則:

假設所有的遞迴呼叫都能執行。以上面求斐波那契數為例,假如求2000000的斐波那契數,由於遞迴函式呼叫次數過多,超過了分配的棧大小,導致棧溢位,見圖3-1。所以從理論上講,上面的求斐波那契數函式遞迴實現不符合設計法則。之前永旺同學已寫過一篇關於尾遞迴優化文章,相關內容見:iOS objc_msgSend尾呼叫優化機制詳解。 或者將求斐波那契數遞迴實現轉換成for迴圈求值,見圖3-2。

PS:求2000000的斐波那契數只是為了說明這個原則,其實2000000的斐波那契數早已經超過了NSUInteger的上限了,手動滑稽。

4. 合成效益原則:

在求解一個問題的同一例項時,切勿在不同的遞迴呼叫中做重複工作。以第三條原則中遞迴方式求斐波那契數函式為例,求5的斐波那契數,在遞迴過程中的函式呼叫詳情見圖4-1,其中好多工作是重複的。這些重複性工作同樣會消耗系統資源,增加執行時間。例如以第三條原則中遞迴方式求40的斐波那契數時,大概耗時2秒見圖4-2,若以for方法求40的斐波那契數,耗時0.0002秒見圖4-3,差距顯而易見。

Demo原始碼:GitHub

結語:遞迴的實現並沒有想象中那麼複雜,關鍵是牢記上述四條基本原則。一個好的遞迴會使程式的實現更加簡單明瞭,選擇使用遞迴還是其他方式解決問題,除了具體問題具體分析,還要見仁見智了。

PS:本文用斐波那契數列當做遞迴例子,而實際求斐波那契數列需要使用矩陣乘法的知識。

關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)

推薦文章:
在iOS 12中無法獲取WiFi的SSID了?別慌!
Web安全漏洞之CSRF
奇舞週刊276期

相關文章