C++程序 求解斐波那契数列
斐波那契数列是以下整数序列。
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ……..
在数学上,斐波那契数列的Fn定义为如下递归关系:
并且有以下初始值:
给定数字n,输出第n个斐波那契数。
示例:
输入:n = 2
输出:1
输入:n = 9
输出:34
编写一个函数 int fib(int n) 返回F n 。例如,如果 n = 0,那么 fib() 应该返回0。如果n = 1,那么它应该返回1。对于n > 1,它应该返回F n-1 + F n-2
对于n = 9
输出:34
下面是获得第n个斐波那契数的不同方法。
方法1(使用递归)
一个简单的直接递归实现数学递推关系的方法如上所述。
输出
时间复杂度 : 指数级别,因为每个函数调用两个其他函数。
如果原始递归树被实现,则将是此树,但现在对于n次调用递归函数。
原始递归树
用于上面代码的递归的优化树
fib(5)
fib(4)
fib(3)
fib(2)
fib(1)
额外的空间: 如果我们考虑函数调用堆栈大小,则为O(n),否则为O(1)。
方法2(使用动态规划) 我们可以通过存储到目前为止计算的斐波那契数来避免方法1中重复的计算。
输出
时间复杂度: 对于给定的n是O(n)
辅助空间: O(n)
方法3:(空间优化方法2)
我们可以通过仅存储前两个数字来优化方法2中使用的空间,因为这是我们需要获取系列中下一个Fibonacci数的全部内容。
输出
时间复杂度: O(n)
额外空间: O(1)
方法4:使用矩阵{{1,1},{1,0}}的幂
这是另一个O(n)的方法,它依赖于这样一个事实:如果我们n次将大小为2 * 2的矩阵M ={{1,1},{1,0}}相乘(换句话说,计算power(M,n)),那么我们得到第(n + 1)个Fibonacci数作为结果矩阵中的行和列(0, 0)处的元素。
矩阵表示为Fibonacci数提供了以下封闭表达式:
输出
时间复杂度: O(n)
额外空间: O(1)
方法5:(优化方法4)
方法4可以优化为O(Logn)的时间复杂度。我们可以在先前的方法中进行递归乘法,以获取power(M,n)(类似于本文中所做的优化)
输出
时间复杂度: O(logn)
额外空间: 如果考虑函数调用堆栈大小,则为O(logn),否则为O(1)。
第6种方法:(O(Log n)时间)
以下是另一个有趣的递推公式,可以用来在O(Log n)时间内找到n’th Fibonacci数。
这个公式是如何工作的?
该公式可以从上述矩阵方程中推导出来。
两边都进行行列式运算,我们得到
(-1)n = Fn+1Fn-1 – Fn²
此外,由于对于任何平方矩阵A,AnAm=An+m,
可以得到以下标识(它们是从矩阵乘积的两个不同系数得到的)
FmFn+Fm-1Fn-1=Fm+n-1—————————-(1)
将n=n+1放入方程(1)中,
FmFn+1+Fm-1Fn=Fm+n—————————-(2)
在等式(1)中将m=n,
F2n-1=Fn²+Fn-1²
在等式(2)中放置m=n,
F2n=(Fn-1+Fn+1)Fn=(2Fn-1+Fn)Fn(来源:Wiki)——–
(通过放置Fn + 1 = Fn + Fn-1)
要得到要证明的公式,我们只需要执行以下操作
如果n是偶数,则可以放置k = n / 2
如果n是奇数,则可以放置k =(n + 1)/ 2
下面是上述思路的实现。
输出
时间复杂度: O(Log n),因为我们在每次递归调用中把问题分成两半。
方法7:(将Binet的公式作为方法)
在这种方法中,我们直接实现斐波那契数列中第n项的公式。
F n = {[(√5 + 1)/2] ^ n} / √5
注意: 上面的公式只针对n<71有效。因为随着n的增加,四舍五入误差会变得显著大。但是,对于n=71,使用floor函数而不是round函数将会得到正确的结果。但是,在n=72之后,这个公式也会失败。
例如:对于n=72,正确结果是498454011879264,但是上面的公式得出498454011879265。
输出
时间复杂度 : O(logn),这是因为计算phi^n需要logn的时间
辅助空间 : O(1)
方法8:DP(自上而下方法)使用记忆化技术
我们可以通过存储到目前为止计算出来的斐波那契数来避免方法1中重复的工作。我们只需要将所有值存储在一个数组中。
输出