我遇到了一些计算斐波那契的有效时间复杂度的方法,以下是其中的一些 -
方法 1 - 动态规划
现在这里的子结构是众所周知的,因此我将直接跳到解决方案 -
static int fib(int n)
{
int f[] = new int[n+2]; // 1 extra to handle case, n = 0
int i;
f[0] = 0;
f[1] = 1;
for (i = 2; i <= n; i++)
{
f[i] = f[i-1] + f[i-2];
}
return f[n];
}
上面的空间优化版本可以如下完成 -
static int fib(int n)
{
int a = 0, b = 1, c;
if (n == 0)
return a;
for (int i = 2; i <= n; i++)
{
c = a + b;
a = b;
b = c;
}
return b;
}
方法2-(利用矩阵{{1,1},{1,0}}的幂)
这是一个 O(n) 依赖于这样一个事实:如果我们将矩阵 M = {{1,1},{1,0}} 乘以 n 次(换句话说,计算 power(M, n ) ),然后我们得到第 (n+1) 个斐波那契数作为结果矩阵中行和列 (0, 0) 处的元素。这个解决方案需要 O(n) 时间。
矩阵表示给出了斐波那契数的以下封闭表达式:
斐波那契矩阵
static int fib(int n)
{
int F[][] = new int[][]{{1,1},{1,0}};
if (n == 0)
return 0;
power(F, n-1);
return F[0][0];
}
/*multiplies 2 matrices F and M of size 2*2, and
puts the multiplication result back to F[][] */
static void multiply(int F[][], int M[][])
{
int x = F[0][0]*M[0][0] + F[0][1]*M[1][0];
int y = F[0][0]*M[0][1] + F[0][1]*M[1][1];
int z = F[1][0]*M[0][0] + F[1][1]*M[1][0];
int w = F[1][0]*M[0][1] + F[1][1]*M[1][1];
F[0][0] = x;
F[0][1] = y;
F[1][0] = z;
F[1][1] = w;
}
/*function that calculates F[][] raise to the power n and puts the
result in F[][]*/
static void power(int F[][], int n)
{
int i;
int M[][] = new int[][]{{1,1},{1,0}};
// n - 1 times multiply the matrix to {{1,0},{0,1}}
for (i = 2; i <= n; i++)
multiply(F, M);
}
这可以优化为在 O(Logn) 时间复杂度下工作。我们可以在前面的方法中进行递归乘法得到power(M, n)。
static int fib(int n)
{
int F[][] = new int[][]{{1,1},{1,0}};
if (n == 0)
return 0;
power(F, n-1);
return F[0][0];
}
static void multiply(int F[][], int M[][])
{
int x = F[0][0]*M[0][0] + F[0][1]*M[1][0];
int y = F[0][0]*M[0][1] + F[0][1]*M[1][1];
int z = F[1][0]*M[0][0] + F[1][1]*M[1][0];
int w = F[1][0]*M[0][1] + F[1][1]*M[1][1];
F[0][0] = x;
F[0][1] = y;
F[1][0] = z;
F[1][1] = w;
}
static void power(int F[][], int n)
{
if( n == 0 || n == 1)
return;
int M[][] = new int[][]{{1,1},{1,0}};
power(F, n/2);
multiply(F, F);
if (n%2 != 0)
multiply(F, M);
}
方法3(O(log n)时间)
下面是一个更有趣的递归公式,可用于在 O(log n) 时间内找到第 n 个斐波那契数。
如果 n 为偶数,则 k = n/2:
F(n) = [2*F(k-1) + F(k)]*F(k)
如果 n 是奇数,则 k = (n + 1)/2
F(n) = F(k)*F(k) + F(k-1)*F(k-1)
这个公式是如何工作的?
该公式可以从上述矩阵方程推导出来。
斐波那契矩阵
取两边的行列式,我们得到
(-1)n = Fn+1Fn-1 – Fn2
此外,由于任意方阵 A 的 AnAm = An+m,因此可以推导出以下恒等式(它们是从矩阵乘积的两个不同系数中获得的)
FmFn + Fm-1Fn-1 = Fm+n-1
通过设置 n = n+1,
FmFn+1 + Fm-1Fn = Fm+n
设 m = n
F2n-1 = Fn2 + Fn-12
F2n = (Fn-1 + Fn+1)Fn = (2Fn-1 + Fn)Fn(来源:Wiki)
为了得到要证明的公式,我们只需要执行以下操作
如果 n 是偶数,我们可以设 k = n/2
如果 n 是奇数,我们可以设 k = (n+1)/2
public static int fib(int n)
{
if (n == 0)
return 0;
if (n == 1 || n == 2)
return (f[n] = 1);
// If fib(n) is already computed
if (f[n] != 0)
return f[n];
int k = (n & 1) == 1? (n + 1) / 2
: n / 2;
// Applyting above formula [See value
// n&1 is 1 if n is odd, else 0.
f[n] = (n & 1) == 1? (fib(k) * fib(k) +
fib(k - 1) * fib(k - 1))
: (2 * fib(k - 1) + fib(k))
* fib(k);
return f[n];
}
方法 4 - 使用公式
在这种方法中,我们直接实现了斐波那契数列中第 n 项的公式。时间 O(1) 空间 O(1)
Fn = {[(√5 + 1)/2] ^ n} / √5
static int fib(int n) {
double phi = (1 + Math.sqrt(5)) / 2;
return (int) Math.round(Math.pow(phi, n)
/ Math.sqrt(5));
}
参考:http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html