如何解释缓存的fibonacci算法的复杂性

时间:2016-09-12 08:26:48

标签: javascript algorithm memoization

我试图正确解释缓存的fibonacci算法复杂性。这是代码(enter image description here):

function allFib(n) {
  var memo = [];
  for (var i = 0; i < n; i++) {
    console.log(i + ":" + fib(i, memo))
  }
}

function fib(n, memo) {
  if (n < 0) return 0;
  else if (n === 1) return 1;
  else if (memo[n]) return memo[n];
  memo[n] = fib(n - 1, memo) + fib(n - 2, memo);
  return memo[n];
}

allFib(5);

解决方案取自&#34;破解编码面试&#34;并适应javascript。 所以这是一个不太好的&#34;函数树调用

https://jsfiddle.net/msthhbgy/2/

我这样想:&#34;最左边的分支(粗体)是评估发生的地方&#34;它绝对是第一次传递给allFib函数的数字。所以复杂性是O(n)。右边的所有内容都将从缓存中获取,不需要额外的函数调用&#34;。这是对的吗?还有如何将它连接到树&#34;理论&#34;。在这种情况下树的深度和高度是4但不是5(接近n但不是它)。我希望答案不直观但更可靠。

2 个答案:

答案 0 :(得分:1)

首先,检查否定n,并将值移至零。

然后检查值是否被缓存,取值。如果不是,请将值分配给缓存并返回结果。

针对n === 0n === 1分配n的特殊情况。

&#13;
&#13;
function fibonacci(number) {
    function f(n) {
        return n in cache ?
            cache[n] :
            cache[n] = n === 0 || n === 1 ? n : f(n - 1) + f(n - 2);
    }

    var cache = [];
    return f(number);
}

console.log(fibonacci(15));
console.log(fibonacci(5));
&#13;
&#13;
&#13;

部分使用cache中的预定义值,建议使用Thomas

&#13;
&#13;
function fibonacci(number) {
    function f(n) {
        return n in cache ?
            cache[n] :
            cache[n] = f(n - 1) + f(n - 2);
    }

    var cache = [0, 1];
    return f(number);
}

console.log(fibonacci(15));
console.log(fibonacci(5));
&#13;
&#13;
&#13;

答案 1 :(得分:1)

这是一个真正使用缓存的函数:

&#13;
&#13;
function Fibonacci() {
  var memo = [0, 1];

  this.callCount = 0;

  this.calc = function(n) {
    this.callCount++;
    return n <= 0 ? 0 
         : memo[n] || (memo[n] = this.calc(n - 1) + this.calc(n - 2));
  }
}

var fib = new Fibonacci();

console.log('15! = ', fib.calc(15));
console.log('calls made: ', fib.callCount);
fib.callCount = 0; // reset counter
console.log('5! = ', fib.calc(5));
console.log('calls made: ', fib.callCount);
fib.callCount = 0;
console.log('18! = ', fib.calc(18));
console.log('calls made: ', fib.callCount);
&#13;
&#13;
&#13;

进行的函数调用次数为:

(n - min(i,n))*2+1

i 备忘录中的最后一项。

您可以通过 n = 18 i = 15 的示例看到以下内容:

按此顺序拨打电话:

calc(18)
calc(17)   // this.calc(n-1) with n=18
calc(16)   // this.calc(n-1) with n=17
calc(15)   // this.calc(n-1) with n=16, this can be returned from memo
calc(14)   // this.calc(n-2) with n=16, this can be returned from memo
calc(15)   // this.calc(n-2) with n=17, this can be returned from memo
calc(16)   // this.calc(n-2) with n=18, this can be returned from memo

一般模式是this.calc(n-1)this.calc(n-2)被调用的次数(当然),此外还有原始调用calc(n)

以下是第一次将fib.calc称为fib.calc(5)时的动画。箭头显示所做的调用。向左越多,递归越深。当相应的结果存储在备忘录

中时,气泡会着色

Fibonacci animation

i 是给定的常数时,这显然是 O(n)