亚线性时间内的第 n 个斐波那契数
是否有任何算法可以在亚线性时间内计算第 n 个斐波那契数?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
是否有任何算法可以在亚线性时间内计算第 n 个斐波那契数?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(17)
这是我的递归版本,递归 log(n) 次。我认为以递归形式最容易阅读:
它之所以有效,是因为您可以使用 fib(n-1),fib(n) 计算 fib(n),fib(n-1) -2) 如果 n 为奇数且 n 为偶数,则可以使用
fib(n/2),fib 计算
。fib(n),fib(n-1)
(n/2-1)基本情况和奇数情况很简单。要导出偶数情况,请从 a、b、c 作为连续的斐波那契值(例如 8、5、3)开始,并将它们写入矩阵中,其中 a = b+c。注意:
从中,我们看到前三个斐波那契数的矩阵乘以任意三个连续斐波那契数的矩阵,等于下一个。所以我们知道:
所以:
简化右侧会导致偶数情况。
Here's my recursive version that recurses log(n) times. I think that it's easiest to read in the recursive form:
It works because you can compute
fib(n),fib(n-1)
usingfib(n-1),fib(n-2)
if n is odd and if n is even, you can computefib(n),fib(n-1)
usingfib(n/2),fib(n/2-1)
.The base case and the odd case are simple. To derive the even case, start with a,b,c as consecutive fibonacci values (eg, 8,5,3) and write them in a matrix, with a = b+c. Notice:
From that, we see that a matrix of the first three fibonacci numbers, times a matrix of any three consecutive fibonacci numbers, equals the next. So we know that:
So:
Simplifying the right hand side leads to the even case.
使用 R
using R
定点运算不准确。 Jason 的 C# 代码对于 n = 71(308061521170130 而不是 308061521170129)及以上给出了错误答案。
要获得正确答案,请使用计算代数系统。 Sympy 就是这样一个 Python 库。 http://live.sympy.org/ 有一个交互式控制台。复制并粘贴此函数
然后计算
您可能想尝试检查
phi
。Fixed point arithmetic is inaccurate. Jason's C# code gives incorrect answer for n = 71 (308061521170130 instead of 308061521170129) and beyond.
For correct answer, use a computational algebra system. Sympy is such a library for Python. There's an interactive console at http://live.sympy.org/ . Copy and paste this function
Then calculate
You might like to try inspecting
phi
.这是一个单行代码,它使用大小为 O(n) 的整数,在 O(log n) 算术运算中计算 F(n):
使用大小为 O(n) 的整数是合理的,因为这与答案的大小相当。
为了理解这一点,让 phi 为黄金比例(x^2=x+1 的最大解),F(n) 为第 n 个斐波那契数,其中 F(0)=0,F(1)=F (2)=1
现在,phi^n = F(n-1) + F(n)phi。
此外,(a+b*phi) 形式的数字(其中 a、b 是整数)在乘法下也是封闭的。
使用这种表示形式,可以通过平方求幂在 O(log n) 整数运算中计算 phi^n。结果将是 F(n-1)+F(n)phi,从中可以读出第 n 个斐波那契数。
请注意,此代码的大部分是标准的乘方求幂函数。
为了得到这个答案的第一行,我们可以注意到,用足够大的整数 X 来表示 phi,可以执行 (a+b*phi)(c+d* phi) 作为整数运算
(a+bX)(c+dX) modulo (X^2-X-1)
。然后,pow
函数可以替换为标准 Pythonpow
函数(该函数方便地包含第三个参数z
,用于计算对取模的结果) z
所选择的X
是2<。
Here's a one-liner that computes F(n), using integers of size O(n), in O(log n) arithmetic operations:
Using integers of size O(n) is reasonable, since that's comparable to size of the answer.
To understand this, let phi be the golden ratio (the largest solution to x^2=x+1) and F(n) be the n'th Fibonacci number, where F(0)=0, F(1)=F(2)=1
Now, phi^n = F(n-1) + F(n)phi.
Also numbers of the form (a+b*phi), where a, b are integers are closed under multiplication.
Using this representation, one can compute phi^n in O(log n) integer operations using exponentiation by squaring. The result will be F(n-1)+F(n)phi, from which one can read off the n'th Fibonacci number.
Note that the majority of this code is a standard exponentiation-by-squaring function.
To get to the one-liner that starts this answer, one can note that representing phi by a large enough integer
X
, one can perform(a+b*phi)(c+d*phi)
as the integer operation(a+bX)(c+dX) modulo (X^2-X-1)
. Then thepow
function can be replaced by the standard Pythonpow
function (which conveniently includes a third argumentz
which calculates the result moduloz
. TheX
chosen is2<<i
.除了通过数学方法进行微调之外,最好的最佳解决方案之一(我认为)是使用字典以避免重复计算。
我们从简单的字典(斐波那契数列的前两个值)开始,并不断将斐波那契值添加到字典中。
前 100000 个斐波那契值大约需要 0.7 秒(Intel Xeon CPU E5-2680 @ 2.70 GHz、16 GB RAM、Windows 10-64 位操作系统)
Apart from fine-tuning by mathematical approaches, one of the best optimum solution (I believe) is using a dictionary in order to avoid repetitive calculations.
We start with trivial dictionary (first two values of Fibonacci sequence) and constantly adding Fibonacci values to the dictionary.
It took about 0.7 seconds for the first 100000 Fibonacci values (Intel Xeon CPU E5-2680 @ 2.70 GHz, 16 GB RAM, Windows 10-64 bit OS)
请参阅分而治之算法此处
链接有这个问题的一些其他答案中提到的矩阵求幂的伪代码。
see divide and conquer algorithm here
The link has pseudocode for the matrix exponentiation mentioned in some of the other answers for this question.
您可以使用奇怪的平方根方程来获得确切的答案。原因是 $\sqrt(5)$ 最后掉了,你只需要用你自己的乘法格式来跟踪系数。
You can use the weird square rooty equation to get an exact answer. The reason is that the $\sqrt(5)$ falls out at the end, you just have to keep track of the coefficients with your own multiplication format.
我遇到过一些以高效时间复杂度计算斐波那契的方法,以下是其中一些 -
方法 1 - 动态规划
现在这里的子结构是众所周知的,因此我将直接跳转到解决方案 -
上述空间优化版本可以按如下方式完成 -
方法 2- (使用矩阵的幂 {{1,1},{ 1,0}} )
这是一个 O(n),它依赖于这样一个事实:如果我们将矩阵 M = {{1,1},{1,0}} 与其自身相乘 n 次(换句话说,计算 power(M, n )),然后我们得到第 (n+1) 个斐波那契数作为结果矩阵中行和列 (0, 0) 的元素。该解决方案的时间复杂度为 O(n)。
矩阵表示给出了以下斐波那契数的闭合表达式:
fibonaccimatrix
这可以优化以 O(Logn) 时间复杂度工作。我们可以在前面的方法中进行递归乘法来得到power(M,n)。
方法 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)
这个公式是如何运作的?
由上述矩阵方程可以推导出该公式。
fibonaccimatrix
两边取行列式,我们得到
(-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
方法 4 - 使用公式
在这个方法中,我们直接实现斐波那契数列第n项的公式。时间 O(1) 空间 O(1)
Fn = {[(√5 + 1)/2] ^ n} / √5
参考:http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html
I have come across some of the methods for calculating Fibonacci with efficient time complexity following are some of them -
Method 1 - Dynamic Programming
Now here the substructure is commonly known hence I'll straightly Jump to the solution -
A space-optimized version of above can be done as follows -
Method 2- ( Using power of the matrix {{1,1},{1,0}} )
This an O(n) which relies on the fact that if we n times multiply the matrix M = {{1,1},{1,0}} to itself (in other words calculate power(M, n )), then we get the (n+1)th Fibonacci number as the element at row and column (0, 0) in the resultant matrix. This solution would have O(n) time.
The matrix representation gives the following closed expression for the Fibonacci numbers:
fibonaccimatrix
This can be optimized to work in O(Logn) time complexity. We can do recursive multiplication to get power(M, n) in the previous method.
Method 3 (O(log n) Time)
Below is one more interesting recurrence formula that can be used to find nth Fibonacci Number in O(log n) time.
If n is even then k = n/2:
F(n) = [2*F(k-1) + F(k)]*F(k)
If n is odd then k = (n + 1)/2
F(n) = F(k)*F(k) + F(k-1)*F(k-1)
How does this formula work?
The formula can be derived from the above matrix equation.
fibonaccimatrix
Taking determinant on both sides, we get
(-1)n = Fn+1Fn-1 – Fn2
Moreover, since AnAm = An+m for any square matrix A, the following identities can be derived (they are obtained from two different coefficients of the matrix product)
FmFn + Fm-1Fn-1 = Fm+n-1
By putting n = n+1,
FmFn+1 + Fm-1Fn = Fm+n
Putting m = n
F2n-1 = Fn2 + Fn-12
F2n = (Fn-1 + Fn+1)Fn = (2Fn-1 + Fn)Fn (Source: Wiki)
To get the formula to be proved, we simply need to do the following
If n is even, we can put k = n/2
If n is odd, we can put k = (n+1)/2
Method 4 - Using a formula
In this method, we directly implement the formula for the nth term in the Fibonacci series. Time O(1) Space O(1)
Fn = {[(√5 + 1)/2] ^ n} / √5
Reference: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html
我们首先应该注意到,斐波那契数
(F(n))
随着n
增长得非常快,并且不能用64 位 来表示
n
大于 93。因此,计算此类n
的程序需要使用额外的机制来运算这些大数。现在,仅考虑(大量)操作的计数,顺序计算它们的算法将需要线性数量的操作。我们可以从以下关于斐波那契数列的恒等式中受益:(
像 A^2 这样的符号表示 A 的平方)。
因此,如果我们知道
F(m)
和F(m+1)
,我们就可以直接计算F(2m)
和F(2m+1)。
考虑
n
的二进制表示。请注意,从x = 1
开始,我们可以通过迭代加倍并可能向x
加 1 来得到x = n
。这可以通过迭代n
的位并检查它是 0 还是 1 来完成。这个想法是,我们可以保持
F(x)
与x
。在每次这样的迭代中,当我们将x
加倍并可能在x
上加 1 时,我们还可以使用以下方法计算F(x)
的新值:F(x)
和F(x+1)
的早期值,具有上述方程。由于迭代次数将以
n
为对数,因此总(大量)操作也以n
为对数。We should first note that Fibonacci numbers
(F(n))
grow very fast withn
and cannot be represented in 64-bits forn
larger than 93. So a program for computing them for suchn
needs to use additional mechanisms to operate on these large numbers. Now, considering only the count of (large-number) operations, the algorithm to sequentially compute them will require linear number of operations.We can benefit from the below identity about Fibonacci numbers:
(a symbol like A^2 denotes square of A).
So, if we know
F(m)
andF(m+1)
, we can directly computeF(2m)
andF(2m+1)
.Consider the binary representation of
n
. Observe that starting withx = 1
, we can makex = n
by iteratively doubling and possibly adding 1 tox
. This can be done by iterating over the bits ofn
, and checking if it is 0 or 1.The idea is that, we can maintain
F(x)
in sync withx
. In each such iteration, as we doublex
and possibly add 1 tox
, we can also compute the new value ofF(x)
using the earlier value ofF(x)
andF(x+1)
, with above equations.Since the number of iterations will be logarithmic in
n
, the total (large-number) operations are also logarithmic inn
.c++14 示例实现:
编译器将生成所有 fib 数字,直到 n = 93
运行时只是一个查找。
要检测调用者的溢出:
c++14 sample implementation:
Compiler will generate all fib numbers until n = 93
Runtime is just a lookup.
To detect overflow from the caller:
对于矩阵
遵循 Pillsy 对矩阵求幂的参考,这样
使用重复乘法求矩阵幂的效率不是很高。
矩阵求幂的两种方法是分而治之,在 O(ln n) 步中产生 Mn,或者特征值分解是恒定时间的,但由于浮点精度有限,可能会引入误差。
如果您想要一个大于浮点实现精度的精确值,则必须使用基于以下关系的 O ( ln n ) 方法:
M 上的特征值分解找到两个矩阵 U 和 Λ 使得 Λ 是对角线并且
Raising a the diagonal matrix Λ to the nth power is a simple matter of raising each element in Λ to the nth, so this gives an O(1) method of raising M to the nth power. However, the values in Λ are not likely to be integers, so some error will occur.
将 2x2 矩阵的 Λ 定义为
要找到每个 λ,我们
求解它给出的
使用二次公式
结果如果您已阅读 Jason 的答案,您可以看到这会发生什么去。
求解特征向量 X1 和 X2:
这些向量给出 U:
使用 U-1 反转 U
由健全性检查给出
:
因此健全性检查成立。
现在我们已经拥有了计算 Mn1,2 所需的一切:
所以
这与其他地方给出的公式一致。
您可以从递归关系中推导出它,但在工程计算和仿真中,计算大型矩阵的特征值和特征向量是一项重要的活动,因为它给出了方程组的稳定性和谐波,并允许有效地将矩阵提升为高幂。
Following from Pillsy's reference to matrix exponentiation, such that for the matrix
then
Raising matrices to powers using repeated multiplication is not very efficient.
Two approaches to matrix exponentiation are divide and conquer which yields Mn in O(ln n) steps, or eigenvalue decomposition which is constant time, but may introduce errors due to limited floating point precision.
If you want an exact value greater than the precision of your floating point implementation, you have to use the O ( ln n ) approach based on this relation:
The eigenvalue decomposition on M finds two matrices U and Λ such that Λ is diagonal and
Raising a the diagonal matrix Λ to the nth power is a simple matter of raising each element in Λ to the nth, so this gives an O(1) method of raising M to the nth power. However, the values in Λ are not likely to be integers, so some error will occur.
Defining Λ for our 2x2 matrix as
To find each λ, we solve
which gives
using the quadratic formula
If you've read Jason's answer, you can see where this is going to go.
Solving for the eigenvectors X1 and X2:
These vectors give U:
Inverting U using
so U-1 is given by
Sanity check:
So the sanity check holds.
Now we have everything we need to calculate Mn1,2:
so
Which agrees with the formula given elsewhere.
You can derive it from a recurrance relation, but in engineering computing and simulation calculating the eigenvalues and eigenvectors of large matrices is an important activity, as it gives stability and harmonics of systems of equations, as well as allowing raising matrices to high powers efficiently.
给出
第 n 个斐波那契数由下式
: 假设原始数学运算 (
+
、-
、*
和/
) 是O(1)
您可以使用此结果来计算n
第Fibonacci 数
O(log n)< /code> 时间(
O(log n)
因为公式中需要求幂)。在 C# 中:
The
n
th Fibonacci number is given bywhere
Assuming that the primitive mathematical operations (
+
,-
,*
and/
) areO(1)
you can use this result to compute then
th Fibonacci number inO(log n)
time (O(log n)
because of the exponentiation in the formula).In C#:
如果您想要确切的数字(这是一个“bignum”,而不是 int/float),那么恐怕
这是不可能的!
如上所述,斐波那契数列的公式为:
fib n
是多少位数字?因为请求的结果是 O em>(n),不能在O(n)时间内计算出来。
如果您只想要答案的低位数字,则可以使用矩阵求幂方法在亚线性时间内进行计算。
If you want the exact number (which is a "bignum", rather than an int/float), then I'm afraid that
It's impossible!
As stated above, the formula for Fibonacci numbers is:
How many digits is
fib n
?Since the requested result is of O(n), it can't be calculated in less than O(n) time.
If you only want the lower digits of the answer, then it is possible to calculate in sub-linear time using the matrix exponentiation method.
SICP 练习之一 是关于这个的,其中描述了答案 here.
在命令式风格中,程序看起来像这样
One of the exercises in SICP is about this, which has the answer described here.
In the imperative style, the program would look something like
您也可以通过对整数矩阵求幂来完成此操作。如果您有矩阵,
则
(M^n)[1, 2]
将等于第n
斐波那契数,如果[] 是矩阵下标,
^
是矩阵求幂。对于固定大小的矩阵,可以以与实数相同的方式在 O(log n) 时间内完成对正积分幂的求幂。编辑:当然,根据您想要的答案类型,您也许可以使用恒定时间算法。与其他公式所示一样,第 n 个斐波那契数随 n 呈指数增长。即使使用 64 位无符号整数,您也只需要一个 94 项的查找表即可覆盖整个范围。
第二次编辑:首先使用特征分解进行矩阵指数与下面的 JDunkerly 解决方案完全相同。该矩阵的特征值是
(1 + sqrt(5))/2
和(1 - sqrt(5))/2
。You can do it by exponentiating a matrix of integers as well. If you have the matrix
then
(M^n)[1, 2]
is going to be equal to then
th Fibonacci number, if[]
is a matrix subscript and^
is matrix exponentiation. For a fixed-size matrix, exponentiation to an positive integral power can be done in O(log n) time in the same way as with real numbers.EDIT: Of course, depending on the type of answer you want, you may be able to get away with a constant-time algorithm. Like the other formulas show, the
n
th Fibonacci number grows exponentially withn
. Even with 64-bit unsigned integers, you'll only need a 94-entry lookup table in order to cover the entire range.SECOND EDIT: Doing the matrix exponential with an eigendecomposition first is exactly equivalent to JDunkerly's solution below. The eigenvalues of this matrix are the
(1 + sqrt(5))/2
and(1 - sqrt(5))/2
.维基百科有一个封闭式解决方案
http://en.wikipedia.org/wiki/Fibonacci_number
或者在 c# 中:
Wikipedia has a closed form solution
http://en.wikipedia.org/wiki/Fibonacci_number
Or in c#:
对于非常大的函数,这个递归函数是有效的。它使用以下方程:
您需要一个可以处理大整数的库。我使用 https://mattmccutchen.net/bigint/ 中的 BigInteger 库。
从一系列斐波那契数开始。使用fibs[0]=0、fibs[1]=1、fibs[2]=1、fibs[3]=2、fibs[4]=3等。在这个例子中,我使用前501个数组(数0)。您可以在此处找到前 500 个非零斐波那契数:http://home.hiwaay.net /~jalison/Fib500.html。需要进行一些编辑才能将其设置为正确的格式,但这并不难。
然后你可以使用这个函数(在 C 语言中)找到任何斐波那契数:
我已经测试了第 25,000 个斐波那契数等。
For really big ones, this recursive function works. It uses the following equations:
You need a library that lets you work with big integers. I use the BigInteger library from https://mattmccutchen.net/bigint/.
Start with an array of of fibonacci numbers. Use fibs[0]=0, fibs[1]=1, fibs[2]=1, fibs[3]=2, fibs[4]=3, etc. In this example, I use an array of the first 501 (counting 0). You can find the first 500 non-zero Fibonacci numbers here: http://home.hiwaay.net/~jalison/Fib500.html. It takes a little editing to put it in the right format, but that is not too hard.
Then you can find any Fibonacci number using this function (in C):
I've tested this for the 25,000th Fibonacci number and the like.