我理解大O符号,但不知道如何计算许多函数的复杂度。特别是,我一直在尝试弄清楚斐波那契数列的朴素版本的计算复杂度:
int Fibonacci(int n)
{
if (n <= 1)
return n;
else
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
斐波那契数列的计算复杂度是多少,如何计算?
我理解大O符号,但不知道如何计算许多函数的复杂度。特别是,我一直在尝试弄清楚斐波那契数列的朴素版本的计算复杂度:
int Fibonacci(int n)
{
if (n <= 1)
return n;
else
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
斐波那契数列的计算复杂度是多少,如何计算?
没有答案能够强调计算这个序列最快且最节省内存的方法。对于斐波那契数列存在一个闭式精确表达式,可以通过使用生成函数或线性代数来找到,接下来我将介绍如何实现。
设 f_1,f_2, ...
是斐波那契数列,其中 f_1 = f_2 = 1
。现在考虑一个二维向量序列
f_1 , f_2 , f_3 , ...
f_2 , f_3 , f_4 , ...
v_{n+1}
,其中M
是一个2x2矩阵,给定为M.v_{n}
。M = [0 1]
[1 1]
由于 f_{n+1} = f_{n+1} and f_{n+2} = f_{n} + f_{n+1}
M 在复数上是对角化的(实际上在实数上也是对角化的,但通常不是这种情况)。M 的两个不同特征向量为
1 1
x_1 x_2
其中x_1 = (1+sqrt(5))/2,x_2 = (1-sqrt(5))/2是多项式方程x*x-x-1 = 0的不同解。相应的特征值为x_1和x_2。将M视为线性变换并更改基础来查看它等效于
D = [x_1 0]
[0 x_2]
为了找到 f_n,请找到 v_n 并查看第一个坐标。要找到 v_n,请将 M 应用于 v_1 n-1 次。但是,应用 M n-1 次很容易,只需将其视为 D。然后使用线性性即可找到
f_n = 1/sqrt(5)*(x_1^n-x_2^n)
由于x_2的范数小于1,随着n趋近于无穷大,相应的项会消失;因此,只需获得比(x_1^n)/sqrt(5)小的最大整数即可准确找到答案。通过利用重复平方的技巧,这可以仅使用O(log_2(n))
乘法(和加法)操作来完成。内存复杂度更为惊人,因为它可以以一种方式实现,您始终只需要在内存中保存一个值小于答案的数字。但是,由于该数字不是自然数,因此此处的内存复杂度取决于是否使用固定位来表示每个数字(因此进行带误差的计算)(在这种情况下为O(1)内存复杂度),或者使用像图灵机这样的更好的模型,在这种情况下需要进行更多的分析。
根据我的看法,这个函数的时间复杂度是O(2^n)
,因为在这个函数中只有递归需要考虑时间(分治)。我们可以看到,上述函数将一直延伸到树的叶子节点,直到我们到达级别F(n-(n-1))
即F(1)
。因此,在这里,当我们记录树的每个深度遇到的时间复杂度时,总和系列为:
1+2+4+.......(n-1)
= 1((2^n)-1)/(2-1)
=2^n -1
这是 2^n [ O(2^n) ]
的顺序。