從斐波那契數(shù)列看遞歸和動態(tài)規(guī)劃

大名鼎鼎的斐波那契數(shù)列:0,1谣拣,1募寨,2,3森缠,5,8仪缸,13贵涵,21......使用數(shù)學歸納法可以看出其規(guī)律為:f(n) = f(n-1) + f(n-2)。

遞歸

下面首先直接使用遞歸(JavaScript實現(xiàn))來求解第 n 項:f(n)

// 直接使用遞歸
let num = 0;    // 用來記錄fib函數(shù)執(zhí)行次數(shù),執(zhí)行一次加一
function fib(n) {
  num ++;
  if(n === 0) {
    return 0;
  }
  if(n === 1) {
    return 1;
  }
  return fib(n-1) + fib(n-2);
}

console.time("time used");
console.log(`result is: ${fib(40)}`);
console.log(`fib() runned ${num} times`);
console.timeEnd("time used");

以 n = 40 為例宾茂,這里我們記錄了 fib 函數(shù)總共調(diào)用的次數(shù)以及運算總共耗時瓷马,結果如下:

可以看出,即便僅僅是計算第 40 項跨晴,fib 函數(shù)調(diào)用的次數(shù)高達3億多次欧聘,時間是2477ms。因為每一次 fib 函數(shù)的調(diào)用都會有兩個子 fib 函數(shù)調(diào)用端盆,那么時間復雜度是 O(2^n) 怀骤,呈指數(shù)級增長,這顯然不是一個好算法焕妙。怎么優(yōu)化呢蒋伦?以一個簡單的例子畫圖分析一下:

上圖是 n = 5 時的遞歸樹,可以看出虛線框中 f(2) 的計算用到了三次焚鹊,同樣的 f(3) 的計算用到了兩次痕届,顯然我們執(zhí)行了非常多的重復運算。那么很自然的想到末患,把每一個狀態(tài)的計算結果都存起來研叫,后面遇到相同的狀態(tài)就可以直接使用了。

記憶化搜索遞歸(自頂向下)

代碼如下璧针,這里第三行 new 了一個長度為 n+1 的數(shù)組蓝撇,用于存放 f(0) 到 f(n) 這 n+1 個狀態(tài)的計算結果:

// 記憶化搜索,記錄每次計算的結果
let num = 0; // 用來記錄fib函數(shù)執(zhí)行次數(shù)陈莽,執(zhí)行一次加一
let totalnum = 40;
let memory = new Array(totalnum+1).fill(-1);
function fib(n) {
  num++;
  if(n === 0) {
    return 0;
  }
  if(n === 1) {
    return 1;
  }
  if(memory[n] === -1) {
    memory[n] = fib(n-1) + fib(n-2);  // 如果前面已經(jīng)得到渤昌,直接使用
  } 
  return memory[n];
}

console.time("timer");
console.log(`result is: ${fib(totalnum)}`);
console.log(`fib() runned ${num} times`);
console.timeEnd("timer");

同樣 n = 40,結果如下:

可以看處出優(yōu)化是十分可觀的走搁,記錄下每一次子調(diào)用的結果独柑,讓算法復雜度從 O(2^n) 變成了 O(n)。這其實就是動態(tài)規(guī)劃的思想私植。什么是動態(tài)規(guī)劃忌栅?

Dynamic programming is when you use past knowledge to make solving a future problem easier.(動態(tài)規(guī)劃是用已知項去更好的求解未知項)

Dynamic programming is a technique used to avoid computing multiple time the same subproblem in a recursive algorithm.

將原問題拆解成若干子問題,同時保存子問題的答案曲稼,使得每個子問題只求解一次索绪,最終獲得原問題的答案。

以上是我看到的兩個很好的定義贫悄。記憶化搜索遞歸求斐波那契數(shù)列顯然是使用了動態(tài)規(guī)劃的思想瑞驱,并且,這是一種自頂向下的求解方式(我們沒有從最基本的問題開始求解窄坦,對于 f(n) = f(n-1) + f(n-2) 唤反,先假定 f(n-1) 和 f(n-2) 是已知的)凳寺。另外我們可以采用另一種自下向上的方式求解,即迭代彤侍,這也是一種動態(tài)規(guī)劃的思想肠缨。

迭代法(自下向上)

代碼如下,我們使用迭代盏阶,f(2) = f(1) + f(0)晒奕,f(3) = f(2) + f(1),.....名斟,顯然這是一種從基礎問題開始的自下向上的解決方法:

let num = 0;
function fib(n) {
  num++;
  let memory = new Array(n+1);
  memory[0] = 1;
  memory[1] = 1;
  for(let i = 2; i <= n; i++) {
    memory[i] = memory[i-1] + memory[i-2];  
  }
  return memory[n];
}

console.time("timer");
console.log(`result is: ${fib(40)}`);
console.log(`fib() runned ${num} times`);
console.timeEnd("timer");

結果如下脑慧,顯然使用迭代的方法復雜度也為 O(n):

小結

動態(tài)規(guī)劃就是:將原問題拆解成若干子問題,同時保存子問題的答案,使得每個子問題只求解一次,最終獲得原問題的答案稚配。對于斐波那契數(shù)列的求解,有自頂向下的記憶化搜索遞歸和自下向上的迭代法霜运,他們都使用了動態(tài)規(guī)劃的思想。

參考鏈接:
https://stackoverflow.com/questions/1065433/what-is-dynamic-programming

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蒋腮,一起剝皮案震驚了整個濱河市淘捡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌池摧,老刑警劉巖焦除,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異作彤,居然都是意外死亡膘魄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門竭讳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來创葡,“玉大人,你說我怎么就攤上這事绢慢〔涌剩” “怎么了?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵胰舆,是天一觀的道長骚露。 經(jīng)常有香客問我,道長缚窿,這世上最難降的妖魔是什么棘幸? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮滨攻,結果婚禮上够话,老公的妹妹穿的比我還像新娘蓝翰。我一直安慰自己光绕,他們只是感情好女嘲,可當我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诞帐,像睡著了一般欣尼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上停蕉,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天愕鼓,我揣著相機與錄音,去河邊找鬼慧起。 笑死菇晃,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蚓挤。 我是一名探鬼主播磺送,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼灿意!你這毒婦竟也來了估灿?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤缤剧,失蹤者是張志新(化名)和其女友劉穎馅袁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荒辕,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡汗销,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了抵窒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弛针。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖估脆,靈堂內(nèi)的尸體忽然破棺而出钦奋,到底是詐尸還是另有隱情,我是刑警寧澤疙赠,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布付材,位于F島的核電站,受9級特大地震影響圃阳,放射性物質(zhì)發(fā)生泄漏厌衔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一捍岳、第九天 我趴在偏房一處隱蔽的房頂上張望富寿。 院中可真熱鬧睬隶,春花似錦、人聲如沸页徐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽变勇。三九已至恤左,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間搀绣,已是汗流浹背飞袋。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留链患,地道東北人巧鸭。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像麻捻,于是被迫代替她去往敵國和親纲仍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,566評論 2 349

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