在 N 为 10^19 时获得第 N 个斐波那契数,如果你以天真的方式去做(至少我猜它不会起作用),那是行不通的。
有一个很多更好的方法来做到这一点。这种技术适用于很多这样的系列。它被称为Fibonacci Q Matrix。
在哪里
这样想:
您有一些矩阵将向量 A 转换为 B:
填写这些条目很容易。特殊之处在于它现在是一个矩阵运算符,所以如果我们想要第 1000 个斐波那契数,我们只需要进行矩阵乘法即可。
你可以用一个循环来做到这一点,但是你需要很长时间才能达到 10^19,并且做 10^19 矩阵乘法(即使它们很小)也需要也有一段时间。
相反,我们采用了另一条捷径。 x^N 可以重写为它们总和为 N 的幂的乘积,即
x**100 == x**90 * x**10
所以目标是在不进行大量计算的情况下在索引中获得大量数字:
x**2 和x*x 一样难——它们花费的时间相同。但是x*x*x*x 给出了与(x**2)**2 相同的答案,但需要额外的乘法。当您获得更高的权力时,收益会变得更多。因此,如果您将指数分解为 2 的幂(任何幂都有效,但这是最简单的情况),
X**100 == X**64 * X**32 * X**4
即
X**100 == (((((X**2)**2)**2)**2)**2)**2 + ...
所以你要做的就是计算出你想要达到的总功率的二的幂,然后取 Q 矩阵的二的幂的乘积。
这似乎对我有用:
fib_matrix = [[1,1],
[1,0]]
def matrix_square(A, mod):
return mat_mult(A,A,mod)
def mat_mult(A,B, mod):
if mod is not None:
return [[(A[0][0]*B[0][0] + A[0][1]*B[1][0])%mod, (A[0][0]*B[0][1] + A[0][1]*B[1][1])%mod],
[(A[1][0]*B[0][0] + A[1][1]*B[1][0])%mod, (A[1][0]*B[0][1] + A[1][1]*B[1][1])%mod]]
def matrix_pow(M, power, mod):
#Special definition for power=0:
if power <= 0:
return M
powers = list(reversed([True if i=="1" else False for i in bin(power)[2:]])) #Order is 1,2,4,8,16,...
matrices = [None for _ in powers]
matrices[0] = M
for i in range(1,len(powers)):
matrices[i] = matrix_square(matrices[i-1], mod)
result = None
for matrix, power in zip(matrices, powers):
if power:
if result is None:
result = matrix
else:
result = mat_mult(result, matrix, mod)
return result
print matrix_pow(fib_matrix, 10**19, 1000000007)[0][1]
然后,你可以更进一步 - 它只是一个 2x2 矩阵,所以我们可以对角化它,然后得到第 n 个斐波那契数的公式,就像 n 的函数一样 - 没有递归。像这样:
如上所述,我们计算将我们从一个步骤带到下一步的矩阵:
然后是从一组数字到下一组数字的关系:
我们可以在哪里链接这些矩阵乘法:
没有什么可以阻止我们回到第一个斐波那契数字:
现在游戏变成了“我们如何将矩阵提升到 n 次方”——这正是上面代码中所做的。但是有比我上面提出的解决方案更好的方法。我们可以将 Q 矩阵分解为特征值和向量,这样写:
其中 U 是一个酉矩阵,包含 Q 的特征值,而 Λ是对应特征值的矩阵。这些特征值和向量是:
然后你使用这种分解方式的标准优势之一,当你将它提升到一个幂时,相邻的 U 矩阵和它的逆组合得到酉矩阵,留下一个 U 并且它是逆的在末端,中间有一系列对角矩阵,将它们提升到幂是微不足道的:
所以现在我们有了用一个公式来写第 n 个斐波那契数所需的一切,没有递归。我会在明天/本周晚些时候完成它...