為什么遞歸效率比迭代差那么多不翩?

遞歸在解決某些問題的時(shí)候使得我們思考的方式得以簡化兵扬,代碼也更加精煉,容易閱讀口蝠。那么既然遞歸有這么多的優(yōu)點(diǎn)器钟,我們是不是什么問題都要用遞歸來解決呢?難道遞歸就沒有缺點(diǎn)嗎妙蔗?今天我們就來討論一下遞歸的不足之處傲霸。談到遞歸就不得不面對它的效率問題。

為什么遞歸是低效的

還是拿斐波那契(Fibonacci)數(shù)列來做例子灭必。在很多教科書或文章中涉及到遞歸或計(jì)算復(fù)雜性的地方都會將計(jì)算斐波那契數(shù)列的程序作為經(jīng)典示例狞谱。如果現(xiàn)在讓你以最快的速度用C#寫出一個(gè)計(jì)算斐波那契數(shù)列第n個(gè)數(shù)的函數(shù)(不考慮參數(shù)小于1或結(jié)果溢出等異常情況),我不知你的程序是否會和下列代碼類似:

1publicstaticulong Fib(ulong n)

2{

3return(n == 1 || n == 2) ? 1 : Fib(n - 1) + Fib(n - 2);

4}

這段代碼應(yīng)該算是短小精悍(執(zhí)行代碼只有一行)禁漓,直觀清晰跟衅,而且非常符合許多程序員的代碼美學(xué),許多人在面試時(shí)寫出這樣的代碼可能心里還會暗爽播歼。但是如果用這段代碼試試計(jì)算Fib(1000)我想就再也爽不起來了伶跷,它的運(yùn)行時(shí)間也許會讓你抓狂。

看來好看的代碼未必中用秘狞,如果程序在效率不能接受那美觀神馬的就都是浮云了叭莫。如果簡單分析一下程序的執(zhí)行流,就會發(fā)現(xiàn)問題在哪烁试,以計(jì)算Fibonacci(5)為例:

從上圖可以看出雇初,在計(jì)算Fib(5)的過程中,F(xiàn)ib(1)計(jì)算了兩次减响、Fib(2)計(jì)算了3次靖诗,F(xiàn)ib(3)計(jì)算了兩次,本來只需要5次計(jì)算就可以完成的任務(wù)卻計(jì)算了9次支示。這個(gè)問題隨著規(guī)模的增加會愈發(fā)凸顯刊橘,以至于Fib(1000)已經(jīng)無法再可接受的時(shí)間內(nèi)算出。

我們當(dāng)時(shí)使用的是簡單的用定義來求 fib(n)颂鸿,也就是使用公式 fib(n) = fib(n-1) + fib(n-2)促绵。這樣的想法是很容易想到的,可是仔細(xì)分析一下我們發(fā)現(xiàn),當(dāng)調(diào)用fib(n-1)的時(shí)候败晴,還要調(diào)用fib(n-2)浓冒,也就是說fib(n-2)調(diào)用了兩次,同樣的道理位衩,調(diào)用f(n-2)時(shí)f(n-3)也調(diào)用了兩次裆蒸,而這些冗余的調(diào)用是完全沒有必要的熔萧√锹浚可以計(jì)算這個(gè)算法的復(fù)雜度是指數(shù)級的。

改進(jìn)的斐波那契遞歸算法

那么計(jì)算斐波那契數(shù)列是否有更好的遞歸算法呢佛致? 當(dāng)然有贮缕。讓我們來觀察一下斐波那契數(shù)列的前幾項(xiàng):

11, 1, 2, 3, 5, 8, 13, 21, 34, 55 …

注意到?jīng)]有,如果我們?nèi)サ羟懊嬉豁?xiàng)俺榆,得到的數(shù)列依然滿足f(n) = f(n-1) – f(n-2), (n>2)感昼,而我們得到的數(shù)列是以1,2開頭的罐脊。很容易發(fā)現(xiàn)這個(gè)數(shù)列的第n-1項(xiàng)就是原數(shù)列的第n項(xiàng)定嗓。怎么樣,知道我們該怎么設(shè)計(jì)算法了吧萍桌?我們可以寫這樣的一個(gè)函數(shù)宵溅,它接受三個(gè)參數(shù),前兩個(gè)是數(shù)列的開頭兩項(xiàng)上炎,第三個(gè)是我們想求的以前兩個(gè)參數(shù)開頭的數(shù)列的第幾項(xiàng)恃逻。

1intfib_i(inta,intb,intn);

在函數(shù)內(nèi)部我們先檢查n的值,如果n為3則我們只需返回a+b即可藕施,這是簡單情境寇损。如果n>3,那么我們就調(diào)用f(b, a+b, n-1)裳食,這樣我們就縮小了問題的規(guī)模(從求第n項(xiàng)變成求第n-1項(xiàng))矛市。好了,最終代碼如下:

1intfib_i(inta,intb ,intn)

2{

3if(n == 3)

4returna+b;

5else

6returnfib_i(b, a+b, n-1);

7}

這樣得到的算法復(fù)雜度是O(n)的诲祸。已經(jīng)是線性的了浊吏。它的效率已經(jīng)可以與迭代算法的效率相比了,但由于還是要反復(fù)的進(jìn)行函數(shù)調(diào)用烦绳,還是不夠經(jīng)濟(jì)卿捎。

遞歸與迭代的效率比較

我們知道,遞歸調(diào)用實(shí)際上是函數(shù)自己在調(diào)用自己径密,而函數(shù)的調(diào)用開銷是很大的午阵,系統(tǒng)要為每次函數(shù)調(diào)用分配存儲空間,并將調(diào)用點(diǎn)壓棧予以記錄。而在函數(shù)調(diào)用結(jié)束后底桂,還要釋放空間植袍,彈棧恢復(fù)斷點(diǎn)籽懦。所以說于个,函數(shù)調(diào)用不僅浪費(fèi)空間,還浪費(fèi)時(shí)間暮顺。

這樣厅篓,我們發(fā)現(xiàn),同一個(gè)問題捶码,如果遞歸解決方案的復(fù)雜度不明顯優(yōu)于其它解決方案的話羽氮,那么使用遞歸是不劃算的。因?yàn)樗暮芏鄷r(shí)間浪費(fèi)在對函數(shù)調(diào)用的處理上惫恼。在C++中引入了內(nèi)聯(lián)函數(shù)的概念档押,其實(shí)就是為了避免簡單函數(shù)內(nèi)部語句的執(zhí)行時(shí)間小于函數(shù)調(diào)用的時(shí)間而造成效率降低的情況出現(xiàn)。在這里也是一個(gè)道理祈纯,如果過多的時(shí)間用于了函數(shù)調(diào)用的處理令宿,那么效率顯然高不起來。

舉例來說腕窥,對于求階乘的函數(shù)來說粒没,其迭代算法的時(shí)間復(fù)雜度為O(n):

01intfact(n)

02{

03inti;

04intr = 1;

05for(i = 1; i < = n; i++)

06{

07r *= i;

08}

09returnr;

10}

而其遞歸函數(shù)的時(shí)間復(fù)雜度也是O(n):

1intfact_r(n)

2{

3if(n == 0)

4return1;

5else

6returnn * f(n);

7}

但是遞歸算法要進(jìn)行n次函數(shù)調(diào)用,而迭代算法則只需要進(jìn)行n次迭代而已油昂。其效率上的差異是很顯著的革娄。

小結(jié)

由以上分析我們可以看到,遞歸在處理問題時(shí)要反復(fù)調(diào)用函數(shù)冕碟,這增大了它的空間和時(shí)間開銷拦惋,所以在使用迭代可以很容易解決的問題中,使用遞歸雖然可以簡化思維過程安寺,但效率上并不合算厕妖。效率和開銷問題是遞歸最大的缺點(diǎn)。

雖然有這樣的缺點(diǎn)挑庶,但是遞歸的力量仍然是巨大而不可忽視的言秸,因?yàn)橛行﹩栴}使用迭代算法是很難甚至無法解決的(比如漢諾塔問題)。這時(shí)遞歸的作用就顯示出來了迎捺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末举畸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子凳枝,更是在濱河造成了極大的恐慌抄沮,老刑警劉巖跋核,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異叛买,居然都是意外死亡砂代,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門率挣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刻伊,“玉大人,你說我怎么就攤上這事椒功〈废洌” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵蛾茉,是天一觀的道長讼呢。 經(jīng)常有香客問我撩鹿,道長谦炬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任节沦,我火速辦了婚禮键思,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘甫贯。我一直安慰自己吼鳞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布叫搁。 她就那樣靜靜地躺著赔桌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渴逻。 梳的紋絲不亂的頭發(fā)上疾党,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音惨奕,去河邊找鬼雪位。 笑死,一個(gè)胖子當(dāng)著我的面吹牛梨撞,可吹牛的內(nèi)容都是我干的雹洗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卧波,長吁一口氣:“原來是場噩夢啊……” “哼时肿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起港粱,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤螃成,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锈颗,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡顷霹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了击吱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淋淀。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖覆醇,靈堂內(nèi)的尸體忽然破棺而出朵纷,到底是詐尸還是另有隱情,我是刑警寧澤永脓,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布袍辞,位于F島的核電站,受9級特大地震影響常摧,放射性物質(zhì)發(fā)生泄漏搅吁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一落午、第九天 我趴在偏房一處隱蔽的房頂上張望谎懦。 院中可真熱鬧,春花似錦溃斋、人聲如沸界拦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽享甸。三九已至,卻和暖如春梳侨,著一層夾襖步出監(jiān)牢的瞬間蛉威,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工猫妙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓷翻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓割坠,卻偏偏與公主長得像齐帚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子彼哼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容