是否有一种算法可以在亚线性时间内计算第n个斐波那契数?
是否有一种算法可以在亚线性时间内计算第n个斐波那契数?
参考 Pillsy 对矩阵指数的讨论,假设给定矩阵
M = [1 1] [1 0]
那么
fib(n) = Mn1,2
将矩阵连乘以产生幂并不高效。
有两种方法可以进行矩阵指数:分而治之法可在 O(ln n) 步内得到 Mn,或特征值分解法可以在常数时间内得到结果,但由于浮点精度有限可能会出现错误。
如果您希望获得大于浮点实现精度的确切值,则必须使用基于以下关系的 O ( ln n ) 方法:
Mn = (Mn/2)2 如果 n 是偶数 = M·Mn-1 如果 n 是奇数
对 M 进行特征值分解可找到两个矩阵 U 和 Λ,使得 Λ 是对角矩阵,并且有
M = U Λ U-1 Mn = ( U Λ U-1) n = U Λ U-1 U Λ U-1 U Λ U-1 ... n 次 = U Λ Λ Λ ... U-1 = U Λ n U-1将对角矩阵 Λ 的幂次方为 n,只需要将 Λ 中的每个元素取 n 次方即可,因此这提供了一种 O(1) 方法来计算 M 的 n 次方。但是,Λ 中的值不太可能是整数,因此会出现一些误差。
定义我们2x2矩阵的Λ为
Λ = [ λ1 0 ] = [ 0 λ2 ]
要找到每个λ,我们解决
|M - λI| = 0
得到
|M - λI| = -λ ( 1 - λ ) - 1
λ² - λ - 1 = 0
使用二次公式
λ = ( -b ± √ ( b² - 4ac ) ) / 2a = ( 1 ± √5 ) / 2 { λ1, λ2 } = { Φ, 1-Φ } where Φ = ( 1 + √5 ) / 2
如果你已经读了Jason的答案,你就会知道发生了什么。
解出特征向量X1和X2:
如果 X1 = [ X1,1, X1,2 ]
M.X1 1 = λ1X1 X1,1 + X1,2 = λ1 X1,1 X1,1 = λ1 X1,2 => X1 = [ Φ, 1 ] X2 = [ 1-Φ, 1 ]
这些向量给出U:
U = [ X1,1, X2,2 ] [ X1,1, X2,2 ]
= [ Φ, 1-Φ ] [ 1, 1 ]
使用以下方法反转U
A = [ a b ] [ c d ] => A-1 = ( 1 / |A| ) [ d -b ] [ -c a ]
所以 U-1 是由以下公式得出:
U-1 = ( 1 / ( Φ - ( 1 - Φ ) ) [ 1 Φ-1 ] [ -1 Φ ] U-1 = ( √5 )-1 [ 1 Φ-1 ] [ -1 Φ ]
检验结果:
UΛU-1 = ( √5 )-1 [ Φ 1-Φ ] . [ Φ 0 ] . [ 1 Φ-1 ] [ 1 1 ] [ 0 1-Φ ] [ -1 Φ ]
令 Ψ = 1-Φ,另一个特征值
因为 Φ 是方程 λ²-λ-1=0 的根 所以 -ΨΦ = Φ²-Φ = 1 并且 Ψ+Φ = 1 UΛU-1 = ( √5 )-1 [ Φ Ψ ] . [ Φ 0 ] . [ 1 -Ψ ] [ 1 1 ] [ 0 Ψ ] [ -1 Φ ]
= ( √5 )-1 [ Φ Ψ ] . [ Φ -ΨΦ ] [ 1 1 ] [ -Ψ ΨΦ ]
= ( √5 )-1 [ Φ Ψ ] . [ Φ 1 ] [ 1 1 ] [ -Ψ -1 ]
= ( √5 )-1 [ Φ²-Ψ² Φ-Ψ ] [ Φ-Ψ 0 ]
= [ Φ+Ψ 1 ] [ 1 0 ]
= [ 1 1 ] [ 1 0 ]
= M
因此,检验结果是正确的。
现在我们有了计算Mn1,2所需的一切:
Mn = UΛnU-1 = ( √5 )-1 [ Φ Ψ ] . [ Φn 0 ] . [ 1 -Ψ ] [ 1 1 ] [ 0 Ψn ] [ -1 Φ ]
= ( √5 )-1 [ Φ Ψ ] . [ Φn -ΨΦn ] [ 1 1 ] [ -Ψn ΨnΦ ]
= ( √5 )-1 [ Φ Ψ ] . [ Φn Φn-1 ] [ 1 1 ] [ -Ψn -Ψn-1 ] 因为ΨΦ=-1
= ( √5 )-1 [ Φn+1-Ψn+1 Φn-Ψn ] [ Φn-Ψn Φn-1-Ψn-1 ]
所以
fib(n) = Mn1,2 = ( Φn - (1-Φ)n ) / √5
这与其他地方给出的公式相符。
您可以从递归关系推导它,但在工程计算和模拟中,计算大矩阵的特征值和特征向量是一项重要的活动,因为它可以给出方程组的稳定性和谐波,同时还允许高效地将矩阵提高到高次幂。
斐波那契数列的第n
项为
f(n) = Floor(phi^n / sqrt(5) + 1/2)
其中
phi = (1 + sqrt(5)) / 2
假设基本的数学运算符(+
、-
、*
和 /
)的时间复杂度为 O(1)
,你可以使用这个结果以 O(log n)
的时间复杂度计算第 n
个斐波那契数(因为公式中有指数运算)。static double inverseSqrt5 = 1 / Math.Sqrt(5);
static double phi = (1 + Math.Sqrt(5)) / 2;
/* should use
const double inverseSqrt5 = 0.44721359549995793928183473374626
const double phi = 1.6180339887498948482045868343656
*/
static int Fibonacci(int n) {
return (int)Math.Floor(Math.Pow(phi, n) * inverseSqrt5 + 0.5);
}
phi^n / sqrt(5) + 1/2
的下取整,其中phi = (1 + sqrt(5)) / 2
。这是一个事实。其次,我理解其他人关于答案长度为O(n)
的观点,但我已经在我的答案中添加了一条注释,假设原始的数学运算花费恒定时间(我知道除非限制输入,否则它们不是)。我的观点是我们可以用O(log n)
个算术运算找到第n个斐波那契数。 - jason如果你想要精确的数字(这是一个“bignum”,而不是int/float),那么恐怕
是不可能的!
如上所述,斐波那契数列的公式为:
fib n = floor (phin/√5 + 1/2)
fib n ~= phin/√5
fib n
有多少位数字?
numDigits (fib n) = log (fib n) = log (phin/√5) = log phin - log √5 = n * log phi - log √5
numDigits (fib n) = n * const + const
它是O(n)
由于所请求的结果是O(n), 因此它不能在小于O(n)的时间内计算。
如果您只想要答案的低位数字,则可以使用矩阵乘法方法在次线性时间内计算。
您也可以通过对一个整数矩阵进行指数运算来达到同样的效果。如果您有以下矩阵:
/ 1 1 \
M = | |
\ 1 0 /
如果[]
是矩阵下标符号,^
是矩阵幂运算符,则 (M^n)[1,2]
将等于第 n
个斐波那契数。对于固定大小的矩阵,幂运算可以像实数一样在O(log n)时间内完成。
编辑: 当然,根据您想要的回答类型,您可能能够使用常量时间算法轻松解决问题。正如其他公式所示,第 n
个斐波那契数呈指数增长。即使使用64位无符号整数,您只需要一个94个条目的查找表来涵盖整个范围。
第二次编辑:首先进行特征分解来做矩阵幂等价于JDunkerly在下面的解法。该矩阵的特征值为(1 + sqrt(5))/2
和(1 - sqrt(5))/2
。
维基百科提供了斐波那契数列的封闭式解法。 http://en.wikipedia.org/wiki/Fibonacci_number
或者用C#实现:
public static int Fibonacci(int N)
{
double sqrt5 = Math.Sqrt(5);
double phi = (1 + sqrt5) / 2.0;
double fn = (Math.Pow(phi, N) - Math.Pow(1 - phi, N)) / sqrt5;
return (int)fn;
}
F(2n-1) = F(n-1)^2 + F(n)^2
F(2n) = (2*F(n-1) + F(n)) * F(n)
BigUnsigned GetFib(int numfib)
{
int n;
BigUnsigned x, y, fib;
if (numfib < 501) // Just get the Fibonacci number from the fibs array
{
fib=(stringToBigUnsigned(fibs[numfib]));
}
else if (numfib%2) // numfib is odd
{
n=(numfib+1)/2;
x=GetFib(n-1);
y=GetFib(n);
fib=((x*x)+(y*y));
}
else // numfib is even
{
n=numfib/2;
x=GetFib(n-1);
y=GetFib(n);
fib=(((big2*x)+y)*y);
}
return(fib);
}
我已经测试了25000个斐波那契数及其类似数字。
这是我的递归版本,递归了log(n)次。我认为在递归形式下最容易阅读:
def my_fib(x):
if x < 2:
return x
else:
return my_fib_helper(x)[0]
def my_fib_helper(x):
if x == 1:
return (1, 0)
if x % 2 == 1:
(p,q) = my_fib_helper(x-1)
return (p+q,p)
else:
(p,q) = my_fib_helper(x/2)
return (p*p+2*p*q,p*p+q*q)
[1 1] * [a b] = [a+b a]
[1 0] [b c] [a b]
从这个公式中,我们可以看出前三个斐波那契数列的矩阵乘以任意三个连续的斐波那契数列的矩阵等于下一个斐波那契数。因此我们知道:
n
[1 1] = [fib(n+1) fib(n) ]
[1 0] [fib(n) fib(n-1)]
那么:
2n 2
[1 1] = [fib(n+1) fib(n) ]
[1 0] [fib(n) fib(n-1)]
以下是一行代码,使用O(n)大小的整数,在O(log n)算术运算中计算F(n):
for i in range(1, 50):
print(i, pow(2<<i, i, (4<<2*i)-(2<<i)-1)//(2<<i))
def mul(p, q):
return p[0]*q[0]+p[1]*q[1], p[0]*q[1]+p[1]*q[0]+p[1]*q[1]
def pow(p, n):
r=1,0
while n:
if n&1: r=mul(r, p)
p=mul(p, p)
n=n>>1
return r
for i in range(1, 50):
print(i, pow((0, 1), i)[1])
phi = (1 + sqrt(5)) / 2
def f(n):
return floor(phi**n / sqrt(5) + 1/2)
>>> f(10)
55
>>> f(71)
308061521170129
phi
。