1. 程式人生 > >演算法練習01 記憶化斐波那契函式

演算法練習01 記憶化斐波那契函式

題目(2018-11-15)

斐波那契數列指的是類似於下面的數列:

1, 1, 2, 3, 5, 8, 13, ……

也就是,第n個數是由前面兩個數相加而來

完成fibonacci函式,接受n作為引數,可以獲取數列中第n個數,例如:

fibonacci(1) // => 1
fibonacci(2) // => 1
fibonacci(3) // => 2
...

實現

想到的最簡單的實現就是利用遞迴:

const fibonacci = (n) => {
  if (n === 2 || n === 1) {
    return 1
  }
  return fibonacci(n - 1) + fibonacci(n - 2)
}

這樣的問題時,當n比較大的時候(比如500),計算時間過長,程式會失去響應

所以需要進行改進,需要利用快取,空間換區時間,在計算時多傳入一個cache物件,用於快取計算過的資料:

const fibonacci = (n, cache = {}) => {
  if (n <= 2) {
    return cache[n] = 1;
  }
  if (cache[n]) {
    return cache[n];
  }
  return cache[n] = fibonacci(n - 1, cache) + fibonacci(n - 2, cache)
};

console.time('1st');
console.log(fibonacci(1000));
console.timeEnd('1st');
// 4.346655768693743e+208
// 1st: 1.26904296875ms

console.time('2ed');
console.log(fibonacci(1000));
console.timeEnd('2ed')
// 41 4.346655768693743e+208
// 2ed: 0.637939453125ms

通過實驗,這樣是行得通的。

我們還可以進一步優化,由於快取cache在每次計算都是需要反問的,也就是說cache是需要保留在記憶體中的,那麼我們就可以構造一個閉包,讓cache保留在閉包中,供遞迴時使用:

const fibonacci = ((cache = {}) => n => {
  if (cache[n]) {
    return cache[n];
  }
  if (n <= 2) {
    return cache[n] = 1;
  }
  return cache[n] = fibonacci(n - 1) + fibonacci(n - 2);
})();

console.time('1st');
console.log(fibonacci(1000));
console.timeEnd('1st');
// 4.346655768693743e+208
// 1st: 0.6630859375ms

console.time('2ed');
console.log(fibonacci(1000));
console.timeEnd('2ed')
// 41 4.346655768693743e+208
// 2ed: 0.186279296875ms

多次試驗後發現,採用閉包的計算速度還是要快於直接傳參的計算速度的,尤其是非首次計算,相對於首次計算時,由於cache會保留在記憶體中,對計算速度的提高非常大

參考