遞歸系列——遞歸樹與函數記憶化


     遞歸操作是把問題逐漸縮小。

     比如斐波那契數列,其遞歸公式是:

f(n) = f(n-1) + f(n-2)
f(0) = f(1) = 1

   也就是說遞歸時,把問題規模減小時,可能會出現很多重復的子問題。f(0)和f(1)會被重復計算多次。

   我們希望減少無用功,此時可以使用緩存來做。

例如下面的demo:

function f(n) {
    if (n < 2) return 1;
    return f(n - 1) + f(n - 2);
}
console.log(f(10));

輸出如下: console.log: 89;

可以使用緩存改成:

function f(n) {
    var cache = f.cache = f.cache || {};
    if (n in cache) return cache[n];
    if (n < 2) return cache[n] = 1;
    return cache[n] = f(n - 1) + f(n - 2);
}
console.log(f(10));
console.log(f.cache);

輸出如下:

console.log: 89
console.log: 
{
      0: 1
      1: 1
      2: 2
      3: 3
      4: 5
      5: 8
      6: 13
      7: 21
      8: 34
      9: 55
      10: 89
}

使用函數記憶化:

代碼如下:

 function memorize(func){
          var fn = function(){
            var cache = fn.cache;
            var key = [].join.call(arguments);
            if (!(key in cache)) return cache[key] = func.apply(this, arguments);
            return cache[key];
       }
         fn.cache = {};
         return fn;
        }
        var fn = memorize(function(n){
          if (n < 2) return 1;
          return fn(n-1) + fn(n-2);
        });
        console.log(fn(10)); // 89
        console.log(fn.cache); //{0: 1, 1: 1, 2: 2, 3: 3, 4: 5, 5: 8, 6: 13, 7: 21, 8: 34, 9: 55, 10: 89}

memoize是對參數func進行了包裝,優先調用緩存。
緩存里沒有時,運行func,並把結果緩存起來。緩存對象的鍵是參數以逗號相連接的字符串。

比如,3個參數時sum(1, 2, 3),key會變成"1,2,3"。可以如下測試:

 
  var sum = memorize(function(a,b,c){
          return a + b + c;
        })
        console.log(sum(1,2,3)); // 6
        console.log(sum.cache); // {1,2,3: 6}

函數記憶化,是函數式編程中的概念。
函數記憶不一定只針對遞歸,但要求函數是純的。
即輸入確定,輸出就確定,不能對程序狀態搞下小動作。
因為內部利用了緩存,並不是每次都會運行函數。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM