【问题标题】:add a memoization to recursive algorithm为递归算法添加记忆
【发布时间】:2016-04-20 19:50:07
【问题描述】:

我为partitioning a number写了一个函数:

var combinations = function (i) {
    var mem = [];
    function inner(n, r, m) {
        for (var k = m; k <= n; k++) {
            if (k == n) {
                r.push(k);
                mem[r] = 1;

                return mem;
            }
            else {
                var copy = r.slice(0);
                copy.push(k);
                inner(n - k, copy, k);
            }
        }
    }

    return inner(i, [], 1);
}

在第二步中,我想为这个算法添加一个记忆,但想不出正确的实现方式,因为直到最后都没有 return 语句(当指定 return 时,例如在 faactorial 或 fibbinacci 中我可以添加记忆)。 有人能把我带到正确的方向吗?

[编辑] 我需要这个算法尽可能快。这是 codewars 上的 kata 比赛:link 要求它必须在 6000 毫秒内执行,输入高达 330。 这是我能想到的最好的算法,除了如何存储部分结果。

【问题讨论】:

  • n, r, m 代表什么数字?
  • 您是要计算所有分区还是只计算分区数?
  • @Iszk,请参阅更新的答案。 :-) 我添加了一个解释。

标签: javascript recursion memoization


【解决方案1】:

这是一个更简单的代码:

function nr_partitions(n) { return p(n, n); }

function p(sum,largest) {
    if (largest == 0) { return 0; }
    if (sum == 0) { return 1; }
    if (sum < 0) { return 0; }
    return p(sum, largest-1) + p(sum-largest, largest);
}

它使用well-known recurrencep(n,k) = p(n,k-1) + p(n-k, k),其中p(n.k)表示n的分区数,其中最大部分最多为k(例如p(3, 2)=2,因为我们只数 1+1+1,1+2,但不是 3)。对于k=n,我们得到n的所有分区数。

添加 memozation 涉及存储字典映射对 (sum, largest)p(sum, largest)

【讨论】:

    【解决方案2】:

    我会按照以下方式进行:

    var cache = {};
    
    var combinations = function (i) {
        if ( cache[i] ){
            return cache[i];
        };
    
        var mem = [];
        function inner(n, r, m) {
            for (var k = m; k <= n; k++) {
                if (k == n) {
                    r.push(k);
                    mem[r] = 1;
    
                    return mem;
                }
                else {
                    var copy = r.slice(0);
                    copy.push(k);
                    inner(n - k, copy, k);
                }
            }
        }
    
        cache[i] = inner(i, [], 1);
        return cache[i];
    }
    

    但是你必须修改你的算法来使用cache(首先计算最大的项?)

    【讨论】:

      【解决方案3】:

      根据您的其他要求,您可能需要考虑使用具有its own _.memoize function 的 underscore.js。

      记忆的秘密在于它利用了闭包的工作原理。当您在另一个范围内定义函数时,它可以访问该范围内的所有内容。当你将该函数返回到作用域之外的某个地方时,它会引用它可以在作用域内看到的所有内容。

      所以要实现记忆,你需要创建一个返回另一个函数的函数,一个在调用内部函数之前进行记忆检查的函数。

      您的代码将如下所示:

      /**
       * Because we'll be returning "a function that returns a function" below,
       * this needs to be executed immediately so combinations() is just
       * a standalone function.
       */
      var combinations = (function(i) {
      
          /**
           * mem needs to stay outside the scope of your inner function.
           * If it's in a closure like this, JavaScript will keep its value
           * around as long as combinations still exists.
           */ 
          var mem = [];
      
          /**
           * A memoization wrapper should return a memoized function
           */
          return function(i) {
      
              /**
               * Check if mem[i] is set and return it if it has been
               */
              if(mem[i] !== undefined) {
                  console.log('returning memoized value');
                  return mem[i];    
              }
      
              function inner(n, r, m) {
                  for (var k = m; k <= n; k++) {
                      if (k == n) {
                          r.push(k);
                          mem[r] = 1;
      
                          return mem;
                      }
                      else {
                          var copy = r.slice(0);
                          copy.push(k);
                          inner(n - k, copy, k);
                      }
                  }
              }
      
              /**
               * If the value needs to be computed, we can set it at the same time
               * as we return it instead of putting it in a temporary variable.  
               */ 
              console.log('computed');
              return mem[i] = inner(i, [], 1);
          }
      
      }()); /** <--- That's the rest of the automatic execution */
      
      console.log(combinations(5));
      console.log(combinations(5));
      

      【讨论】:

        猜你喜欢
        • 2018-04-19
        • 1970-01-01
        • 2020-12-19
        • 2011-02-04
        • 2022-12-03
        • 1970-01-01
        • 2012-11-12
        • 2012-09-27
        • 1970-01-01
        相关资源
        最近更新 更多