我知道大o符号,但我不知道如何计算它的许多函数。特别是,我一直在试图弄清楚朴素版斐波那契数列的计算复杂度:

int Fibonacci(int n)
{
    if (n <= 1)
        return n;
    else
        return Fibonacci(n - 1) + Fibonacci(n - 2);
}

斐波那契数列的计算复杂度是多少?它是如何计算的?


当前回答

我同意pgaur和rickerbh的观点,递归-fibonacci的复杂度是O(2^n)。

我通过一个相当简单但我相信仍然有效的推理得出了同样的结论。

首先,这完全是关于计算第n个斐波那契数时调用多少次递归斐波那契函数(F()从现在开始)。如果它在0到n的数列中被调用一次,那么我们有O(n),如果它对每个数字被调用n次,那么我们得到O(n*n)或O(n²),以此类推。

因此,当对一个数字n调用F()时,对一个给定的0到n-1之间的数字调用F()的次数随着趋近于0而增加。

作为第一印象,在我看来,如果我们把它放在视觉上,每次绘制一个单位F()被调用为给定的数字,我们会得到一种金字塔形状(也就是说,如果我们将单位水平居中)。就像这样:

n              *
n-1            **
n-2           ****  
...
2           ***********
1       ******************
0    ***************************

现在的问题是,随着n的增长,金字塔的底部扩大的有多快?

让我们举一个真实的例子,比如F(6)

F(6)                 *  <-- only once
F(5)                 *  <-- only once too
F(4)                 ** 
F(3)                ****
F(2)              ********
F(1)          ****************           <-- 16
F(0)  ********************************    <-- 32

我们看到F(0)被调用了32次,也就是2^5,在这个例子中是2^(n-1)

现在,我们想知道F(x)被调用了多少次,我们可以看到F(0)被调用的次数只是其中的一部分。

如果我们在心里把F(6)到F(2)线的所有*移到F(1)线中,我们看到F(1)和F(0)线现在长度相等。这意味着,当n=6 = 2x32=64=2^6时,total乘以F()被调用。

现在,说到复杂性:

O( F(6) ) = O(2^6)
O( F(n) ) = O(2^n)

其他回答

只要问问自己,F(n)需要执行多少条语句才能完成。

对于F(1),答案是1(条件的第一部分)。

对于F(n)答案是F(n-1) + F(n-2)

那么什么函数满足这些规则呢?试试an (a > 1):

==地理==根据美国人口普查,该镇总面积为,其中土地和(1.1%)水。

再除以a(n-2)

A2 == a + 1

解出a,你得到(1+根号(5))/2 = 1.6180339887,也就是黄金比例。

所以需要指数级的时间。

通过绘制递归树可以更好地估计递归算法的时间复杂度,在这种情况下,绘制递归树的递归关系为T(n-1) =T(n- 2)+O(1) 注意,每一步花费O(1)意味着常数时间,因为它只做了一次比较来检查if块中的n值。递归树是这样的

          n
   (n-1)      (n-2)
(n-2)(n-3) (n-3)(n-4) ...so on

这里假设上面树的每一层都用i表示 因此,

i
0                        n
1            (n-1)                 (n-2)
2        (n-2)    (n-3)      (n-3)     (n-4)
3   (n-3)(n-4) (n-4)(n-5) (n-4)(n-5) (n-5)(n-6)

假设在特定的i值处,树就结束了,也就是当n-i=1时,因此i=n-1,也就是说树的高度是n-1。 现在让我们看看树中n层中的每一层做了多少工作。注意,按照递归关系,每一步花费O(1)时间。

2^0=1                        n
2^1=2            (n-1)                 (n-2)
2^2=4        (n-2)    (n-3)      (n-3)     (n-4)
2^3=8   (n-3)(n-4) (n-4)(n-5) (n-4)(n-5) (n-5)(n-6)    ..so on
2^i for ith level

因为i=n-1是树的高度,所以每一层所做的功为

i work
1 2^1
2 2^2
3 2^3..so on

因此,所做的总功将是每一层所做的功的总和,因此它将是2^0+2^1+2^2+2^3…+2^(n-1),因为i=n-1。 通过几何级数,这个和是2^n,因此总时间复杂度是O(2^n)

你可以展开它,有一个可视化

     T(n) = T(n-1) + T(n-2) <
     T(n-1) + T(n-1) 

     = 2*T(n-1)   
     = 2*2*T(n-2)
     = 2*2*2*T(n-3)
     ....
     = 2^i*T(n-i)
     ...
     ==> O(2^n)

通过绘制函数调用图来计算很简单。简单地为n的每个值添加函数调用,看看这个数字是如何增长的。

大O是O(Z^n), Z是黄金比例,约为1.62。

当我们增加n时,列奥纳多数和斐波那契数都接近这个比率。

与其他大O问题不同,输入中没有可变性,算法和算法的实现都是明确定义的。

不需要一堆复杂的数学。简单地画出下面的函数调用,并将函数与数字匹配。

如果你熟悉黄金比例你就能认出来。

这个答案比公认的f(n) = 2^n的答案更正确。永远不会。它会趋于f(n) = golden_ratio^n。

2 (2 -> 1, 0)

4 (3 -> 2, 1) (2 -> 1, 0)

8 (4 -> 3, 2) (3 -> 2, 1) (2 -> 1, 0)
            (2 -> 1, 0)


14 (5 -> 4, 3) (4 -> 3, 2) (3 -> 2, 1) (2 -> 1, 0)
            (2 -> 1, 0)

            (3 -> 2, 1) (2 -> 1, 0)

22 (6 -> 5, 4)
            (5 -> 4, 3) (4 -> 3, 2) (3 -> 2, 1) (2 -> 1, 0)
                        (2 -> 1, 0)

                        (3 -> 2, 1) (2 -> 1, 0)

            (4 -> 3, 2) (3 -> 2, 1) (2 -> 1, 0)
                        (2 -> 1, 0)

它的下端以2^(n/2)为界,上端以2^n为界(如其他注释中所述)。这个递归实现的一个有趣的事实是它本身有一个紧密的Fib(n)渐近界。这些事实可以总结为:

T(n) = Ω(2^(n/2))  (lower bound)
T(n) = O(2^n)   (upper bound)
T(n) = Θ(Fib(n)) (tight bound)

如果你愿意,可以用它的封闭形式进一步简化紧边界。