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

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

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


当前回答

在麻省理工学院有一个关于这个具体问题的很好的讨论。在第5页,他们指出,如果你假设一个加法需要一个计算单位,那么计算Fib(N)所需的时间与Fib(N)的结果密切相关。

因此,你可以直接跳到斐波那契数列的非常接近的近似:

Fib(N) = (1/sqrt(5)) * 1.618^(N+1) (approximately)

因此,假设朴素算法的最坏情况是

O((1/sqrt(5)) * 1.618^(N+1)) = O(1.618^(N+1))

PS:如果你想了解更多信息,维基百科上有关于第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) = 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)

由于计算的重复,朴素递归版本的斐波那契是指数型的:

在根上,你正在计算:

F(n)取决于F(n-1)和F(n-2)

F(n-1)又取决于F(n-2)和F(n-3)

F(n-2)又取决于F(n-3)和F(n-4)

然后你在每一个二级递归调用中都浪费了大量的数据,时间函数看起来像这样:

T(n) = T(n-1) + T(n-2) + C,C 常数

T(n-1) = T(n-2) + T(n-3) > T(n-2) 则

T(n) > 2*T(n-2)

...

T(n) > 2^(n/2) * T(1) = O(2^(n/2))

这只是一个下界,对于你的分析来说应该足够了,但实时函数是一个常数的因子,根据相同的斐波那契公式,封闭形式是已知的黄金比例的指数。

此外,你可以使用动态规划找到优化版的斐波那契函数,如下所示:

static int fib(int n)
{
    /* memory */
    int f[] = new int[n+1];
    int i;

    /* Init */
    f[0] = 0;
    f[1] = 1;

    /* Fill */
    for (i = 2; i <= n; i++)
    {
        f[i] = f[i-1] + f[i-2];
    }

    return f[n];
}

这是优化的,只做n步,但也是指数级的。

Cost functions are defined from Input size to the number of steps to solve the problem. When you see the dynamic version of Fibonacci (n steps to compute the table) or the easiest algorithm to know if a number is prime (sqrt(n) to analyze the valid divisors of the number). you may think that these algorithms are O(n) or O(sqrt(n)) but this is simply not true for the following reason: The input to your algorithm is a number: n, using the binary notation the input size for an integer n is log2(n) then doing a variable change of

m = log2(n) // your real input size

让我们找出作为输入大小的函数的步数

m = log2(n)
2^m = 2^log2(n) = n

那么你的算法的代价作为输入大小的函数是:

T(m) = n steps = 2^m steps

这就是为什么成本是指数级的。

在麻省理工学院有一个关于这个具体问题的很好的讨论。在第5页,他们指出,如果你假设一个加法需要一个计算单位,那么计算Fib(N)所需的时间与Fib(N)的结果密切相关。

因此,你可以直接跳到斐波那契数列的非常接近的近似:

Fib(N) = (1/sqrt(5)) * 1.618^(N+1) (approximately)

因此,假设朴素算法的最坏情况是

O((1/sqrt(5)) * 1.618^(N+1)) = O(1.618^(N+1))

PS:如果你想了解更多信息,维基百科上有关于第n个斐波那契数的封闭形式表达的讨论。

通过绘制递归树可以更好地估计递归算法的时间复杂度,在这种情况下,绘制递归树的递归关系为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)