国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

從斐波那契數列看遞歸和動態規劃

charles_paul / 2728人閱讀

摘要:大名鼎鼎的斐波那契數列,,,,,,,,使用數學歸納法可以看出其規律為。對于斐波那契數列的求解,有自頂向下的記憶化搜索遞歸和自下向上的迭代法,他們都使用了動態規劃的思想。

大名鼎鼎的斐波那契數列:0,1,1,2,3,5,8,13,21...使用數學歸納法可以看出其規律為:f(n) = f(n-1) + f(n-2)

遞歸

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

// 直接使用遞歸
let num = 0;    // 用來記錄fib函數執行次數,執行一次加一
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 函數總共調用的次數以及運算總共耗時,結果如下:

可以看出,即便僅僅是計算第 40 項,fib 函數調用的次數高達3億多次,時間是2477ms。因為每一次 fib 函數的調用都會有兩個子 fib 函數調用,那么時間復雜度是 O(2^n) ,呈指數級增長,這顯然不是一個好算法。怎么優化呢?以一個簡單的例子畫圖分析一下:

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

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

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

// 記憶化搜索,記錄每次計算的結果
let num = 0; // 用來記錄fib函數執行次數,執行一次加一
let totalnum = 40;
let memory = new Array(totalnum).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);  // 如果前面已經得到,直接使用
  } 
  return memory[n];
}

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

同樣 n = 40,結果如下:

可以看處出優化是十分可觀的,記錄下每一次子調用的結果,讓算法復雜度從 O(2^n) 變成了 O(n)。這其實就是動態規劃的思想。什么是動態規劃?

Dynamic programming is when you use past knowledge to make solving a future problem easier.(動態規劃是用已知項去更好的求解未知項)
Dynamic programming is a technique used to avoid computing multiple time the same subproblem in a recursive algorithm.
將原問題拆解成若干子問題,同時保存子問題的答案,使得每個子問題只求解一次,最終獲得原問題的答案。

以上是我看到的兩個很好的定義。記憶化搜索遞歸求斐波那契數列顯然是使用了動態規劃的思想,并且,這是一種自頂向下的求解方式(我們沒有從最基本的問題開始求解,對于 f(n) = f(n-1) + f(n-2) ,先假定 f(n-1) f(n-2) 是已知的)。另外我們可以采用另一種自下向上的方式求解,即迭代,這也是一種動態規劃的思想。

迭代法(自下向上)

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

let num = 0;
function fib(n) {
  num++;
  let memory = new Array(n);
  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):

小結

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

參考鏈接:
https://stackoverflow.com/que...

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95942.html

相關文章

  • 時間復雜度、遞歸、尾調用— (讀文筆記)

    摘要:遞歸算法是一種直接或者間接調用自身函數或者方法的算法。因此,分治策略一般用來解決子問題相互對立的問題,稱為標準分治,而動態規劃用來解決子問題重疊的問題。調用棧尾遞歸和手動優化尾調用就是一個函數執行的最后一步是將另外一個函數調用并返回。 輸出 斐波那契數列的四種寫法 讀參考文章列表 算法復雜度中的O(logN)底數是多少 從斐波那契數列談談代碼的性能優化 冰與火之歌:時間與空間復雜度 ...

    susheng 評論0 收藏0
  • js 實現斐那契數列(數組緩存、動態規劃、尾調用優化)

    摘要:根據該規則,返回第個斐波那契數。尾遞歸函數調用自身,稱為遞歸。一個前端眼中的斐波那契數列解斐波那契數列的實用解法調用棧尾遞歸和手動優化尾調用優化譯我從用寫斐波那契生成器中學到的令人驚訝的件事 斐波那契數列是以下一系列數字: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, ... 在種子數字 0 和 1 ...

    趙連江 評論0 收藏0
  • 動態規劃問題(1)——斐那契數列

    摘要:動態規劃有時被稱為遞歸的相反的技術。動態規劃方案通常使用一個數組來建立一張表,用于存放被分解成眾多子問題的解。今天我們先從我們最熟的斐波那契數列數列開始。斐波那契數列在很多地方都會用上,我是從一個臺階問題發現的,同時知道了動態規劃的解法。 前段時間一直寫了幾個算法題目,發現有個很牛逼的算法,動態規劃,雖然有的解題思路和動態規劃很像,但是當時不知道其中的原理和一些通用性,接下來的幾天,通...

    Eminjannn 評論0 收藏0
  • 那契數列的js方案以及優化

    摘要:在上做了一道斐波那契數列求和的題目,做完之后做了一些簡單的優化和用另一種方法實現。動態規劃解決方案斐波那契數列求和除了可以用遞歸的方法解決,還可以用動態規劃的方法解決。 在codewars上做了一道斐波那契數列求和的題目,做完之后做了一些簡單的優化和用另一種方法實現。 題目 function fibonacci(n) { if(n==0 || n == 1) r...

    xinhaip 評論0 收藏0
  • 數據結構與算法:常見排序算法

    摘要:這是一個簡單的遞歸函數,你可以使用它來生成數列中指定序號的數值這個函數的問題在于它的執行效率非常低有太多值在遞歸調用中被重新計算。 本章內容銜接上一章 數據結構與算法:二分查找 內容提要 兩種基本數據結構: 數組 常見操作: 數組降維、數組去重 鏈表 遞歸:遞歸是很多算法都使用的一種編程方法   - 如何將問題分成基線條件和遞歸條件   - 分而治之策略解決棘手問題 ...

    wuyumin 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<