Big o 大O表示法-O(N)斐波那契发生器

Big o 大O表示法-O(N)斐波那契发生器,big-o,fibonacci,memoization,Big O,Fibonacci,Memoization,只是在面试前复习一下我的大O 在《破解编码访谈》(第六版)第53页和第54页,在关于大O的章节中,您将看到示例15,如下所示 void allFib(int n){ int[] memo = new int[n+1]; for(int i=0; i<n; i++){ System.out.println(i + ":" + fib(i,memo)); } } int fib(int n, int[] memo){ if(n<=0) r

只是在面试前复习一下我的大O

在《破解编码访谈》(第六版)第53页和第54页,在关于大O的章节中,您将看到示例15,如下所示

void allFib(int n){
    int[] memo = new int[n+1];
    for(int i=0; i<n; i++){
        System.out.println(i + ":" + fib(i,memo));
    }
}

int fib(int n, int[] memo){
    if(n<=0) return 0;
    else if(n==1) return 1;
    else if(memo[n]>0) return memo[n];

    memo[n]=fib(n-1,memo)+fib(n-2,memo);

    return memo[n];
}
最终编辑: 谢谢大家。我有我的答案。即使对fib方法的单个调用也受益于记忆,因此不是O(N^2),它也是O(N)。

它是
O(N)
,因为每个值只计算一次,以后再使用计算出的值。例如,对于
fib(4)
这将是调用堆栈:

fib(4) = fib(3) + fib(2);
    fib(3) = fib(2) + fib(1);
        fib(2) = fib(1) + fib(0); // value stored
            fib(1) = 1;
            fib(0) = 1;
    fib(2) = 2; // value accessed
存储值在已计算时被访问

当您看到斐波那契数的“惰性实现”时:

int fib(int i) {
    if(i <= 1) return 1;
    return fib(i - 1) + fib(i - 2);
}
如您所见,
fib(2)
fib(1)
fib(0)
需要多次计算。使用内存方法,每个新值只计算一次。另外,请记住,调用堆栈会随着Fibonacci值的增加而呈指数级增大,这会导致更多的数字“双重计算”。这就是为什么这种惰性方法有
O(2^N)
,而内存方法是
O(N)


HTH

一种看待这一点的方法是在
memo[n]=fib(n-1,memo)+fib(n-2,memo)
fib(n-1,memo)
返回时,
fib(n-2,memo)
所需的值已经存储在
memo
中(
if(memo[n]>0)return memo[n]
),这在每个递归级别都会重复

因此,与调用图看起来像这样不同(对于naive版本):

看起来像这样

         *
        * * 
       * *   
      * *  

你必须小心这里的N
allFib(n)
在O(n)中,其中n是您传递的数字。它不是在O(N)中,其中N是输入的大小(以位为单位)。当然,最好的方法是使用迭代而不是递归。我仍然不确定我是否理解。如果我直接调用fib(n,memo),我必须计算出fib(1),fib(2),fib(3)。。。fib(n)。这将是O(N^2),就像非备忘录版本一样,因为备忘录是空的。添加一个循环来反复调用函数会增加更多的工作,而不是减少。你仍然需要计算fib(1),fib(2)。。。fib(n),但是您有固定的时间工作来添加和检索备忘录中的值。在这两种情况下,n的定义都是你解析它的数字,而不是输入的大小(以位为单位)。现在有意义了,请看最后的编辑和David Soroko的回答。坦率地说,当你提出这样的解决方案时,我不会给你这份工作。迭代法更简单、更直接。假设我们在n=50时调用上面的第二种方法。没有循环调用它50次,只有递归。这是O(N^2),因为备忘录没有用处。它在开始时为空,存储的值永远不会被重新访问。现在用50调用第一个方法。它从1循环到50,调用fib(n)50次。每次它叫它,它做的工作比以前的实验少,但它叫它50次。。。加上从备忘录中添加和检索的O(1)开销。更多的工作,而不是更少,但它是O(N)?当你从fib(49,memo)+fib(48,memo)开始时,首先要执行的是左手呼叫:fib(49,memo),然后fib(48,memo),fib(47,memo),等等。。。当这个调用链(深度为50)开始返回时,每一步都会执行右侧调用并立即返回,因为该值已被memorizedTanks David。这是有道理的。因此,即使我只调用一次具有大n值的fib方法,它仍然会从记忆中受益。
int fib(int i) {
    if(i <= 1) return 1;
    return fib(i - 1) + fib(i - 2);
}
fib(4) = fib(3) + fib(2);
    fib(3) = fib(2) + fib(1);
        fib(2) = fib(1) + fib(0);
            fib(1) = 1;
            fib(0) = 1;
    fib(2) = fib(1) + fib(0);
        fib(1) = 1;
        fib(0) = 1;
        *
       * *
      * * * * 
  * * * * * * * *
         *
        * * 
       * *   
      * *