斐波那契数列算法逐层优化(C++)

时间复杂度:
O ( 1 ) &lt; O ( l o g n ) &lt; O ( n l o g n ) &lt; O ( n 2 ) &lt; O ( n 3 ) &lt; O ( 2 n ) &lt; O ( n ! ) &lt; O ( n n ) O(1)&lt;O(logn)&lt;O(nlogn)&lt;O(n^2)&lt;O(n^3)&lt;O(2^n)&lt;O(n!)&lt;O(n^n) O(1)<O(logn)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)

兔子数列

假设第1个月有1对刚诞生的兔子,第2个月进入成熟期,第3个月开始生育兔子,而1对成熟的兔子每月会生1对兔子,兔子永远不死去……那么,由1对初生兔子开始,12个月后会有多少对兔子?

问题分析

太常见的题目了,就不一一每月举例分析了。
这个数列有明显的特点,从第3个月开始,当月的兔子数=上月兔子数+当月新生兔子数,而当月新生的兔子正好是上上月的兔子数。因此,易得
当月的兔子数=上月兔子数+上上月的兔子数
斐波那契数列如下:1,1,2,3,5,8,13,21,34,……
递归表达式:
F ( n ) = { 1 , n=1 1 , n=2 F ( n − 1 ) + F ( n − 2 ) , n&gt;2 F(n) = \begin{cases} 1, &amp; \text{n=1} \\[2ex] 1, &amp; \text{n=2} \\[2ex] F(n-1)+F(n-2), &amp; \text{n&gt;2} \end{cases} F(n)=1,1,F(n1)+F(n2),n=1n=2n>2

算法1(按照递归表达式设计):

long long Fib1(int n)
{
	if( n<1 ) return -1;
	if( n==1 || n==2 ) return 1;
	return Fib1(n-1)+Fib1(n-2);
}

算法复杂度分析:
假设T(n)表示计算Fib1(n)所需要的基本操作次数,那么:
n=1时,T(n)=1;
n=2时,T(n)=1;
n=3时,T(n)=3;//调用Fib1(2)、Fib1(1)和执行一次加法运算Fib1(2)+Fib1(1)
n>2时,T(n)=T(n-1)+T(n-2)+1;

递归表达式和时间复杂度T(n)之间的关系如下:
F ( n ) = { 1 , n=1,T(n)=1 1 , n=2,T(n)=1 F ( n − 1 ) + F ( n − 2 ) , n&gt;2,T(n)=T(n-1)+T(n-2)+1 F(n) = \begin{cases} 1, &amp; \text{n=1,T(n)=1} \\[2ex] 1, &amp; \text{n=2,T(n)=1} \\[2ex] F(n-1)+F(n-2), &amp; \text{n&gt;2,T(n)=T(n-1)+T(n-2)+1} \end{cases} F(n)=1,1,F(n1)+F(n2),n=1,T(n)=1n=2,T(n)=1n>2,T(n)=T(n-1)+T(n-2)+1
由此可得:T(n)>=F(n)

又斐波那契数列通项为:
F ( n ) = 1 5 ( ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ) F(n)=\frac {1}{\sqrt5}((\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n) F(n)=5 1((21+5 )n(215 )n)

当n趋近于无穷时, F ( n ) ≈ 1 5 ( 1 + 5 2 ) n F(n)\approx\frac {1}{\sqrt5}(\frac{1+\sqrt5}{2})^n F(n)5 1(21+5 )n

发现这里的时间复杂度属于爆炸增量函数

算法2(记录前两项的值):

long long Fib2(int n)
{
	if( n<1 ) return -1;
	int * a = new int[n+1];//定义一个长度为n+1的数组,0空间未使用
	a[1] = 1;
	a[2] = 1;
	for(int i=3;i<=n;++i)
		a[i] = a[i-1]+a[i-2];
	return a[n];
}

算法复杂度分析:
时间复杂度为O(n),从原来的指数阶降到了多项式阶
空间复杂度为O(n)

但我们需要得到第n个斐波那契数,中间结果只是为了下一次使用,根本不需要记录。因此,我们可以在改进算法。

算法3(迭代法):

long long Fib3(int n)
{
	int s1,s2;
	if( n<1 ) return -1;
	if( n==1 || n==2 ) return 1;
	s1 = 1;
	s2 = 1;
	for(int i=3;i<=n;++i)
	{
		s2 += s1;//辗转相加法
		s1 = s2-s1;//记录前一项
	}
	return s2;
}
初始值: s 1 = 1 ; s 2 = 1 ; s_1=1;s_2=1; s1=1;s2=1;当前解记录前一项
i=3时 s 2 = s 1 + s 2 = 2 s_2=s_1+s_2=2 s2=s1+s2=2 s 1 = s 2 − s 1 = 1 s_1=s_2-s_1=1 s1=s2s1=1
i=4时 s 2 = s 1 + s 2 = 3 s_2=s_1+s_2=3 s2=s1+s2=3 s 1 = s 2 − s 1 = 2 s_1=s_2-s_1=2 s1=s2s1=2
i=5时 s 2 = s 1 + s 2 = 5 s_2=s_1+s_2=5 s2=s1+s2=5 s 1 = s 2 − s 1 = 3 s_1=s_2-s_1=3 s1=s2s1=3
i=6时 s 2 = s 1 + s 2 = 8 s_2=s_1+s_2=8 s2=s1+s2=8 s 1 = s 2 − s 1 = 5 s_1=s_2-s_1=5 s1=s2s1=5
………………

算法使用了若干个辅助变量,迭代辗转相加,每次记录前一项,时间复杂度为O(n),但空间复杂度降到了O(1)

但其实斐波那契数列时间复杂度还可以降到对数阶O(logn)。

算法4(利用矩阵的乘法):

数学基础推导先写在这里,由于博主本人代码能力还不过关,后期会补上矩阵代码:
( f ( n ) f ( n − 1 ) ) = ( 1 1 1 0 ) ( f ( n − 1 ) f ( n − 2 ) ) = … … = ( 1 1 1 0 ) n − 2 ( f ( 2 ) f ( 1 ) ) \begin{pmatrix} f(n) \\ f(n-1) \\ \end{pmatrix} = \begin{pmatrix} 1 &amp; 1 \\ 1 &amp; 0 \\ \end{pmatrix} \begin{pmatrix} f(n-1) \\ f(n-2) \\ \end{pmatrix} =……= \begin{pmatrix} 1 &amp; 1 \\ 1 &amp; 0 \\ \end{pmatrix}^{n-2} \begin{pmatrix} f(2) \\ f(1) \\ \end{pmatrix} (f(n)f(n1))=(1110)(f(n1)f(n2))==(1110)n2(f(2)f(1))
计算f(n)即计算 ( 1 1 1 0 ) n − 2 \begin{pmatrix} 1 &amp; 1 \\ 1 &amp; 0 \\ \end{pmatrix}^{n-2} (1110)n2,而计算矩阵的(n-2)次方,又可以进行分解,即计算 ( 1 1 1 0 ) n − 2 2 \begin{pmatrix} 1 &amp; 1 \\ 1 &amp; 0 \\ \end{pmatrix}^\frac{n-2}{2} (1110)2n2的平方,逐步分解下去;由于折半计算矩阵的次方,因而时间复杂度为O(logn)

参考文献:
[1] 陈小玉.趣学算法[M].北京:人民邮电出版社,2017:11-14.
[2] beautyofmath.关于斐波那契数列三种解法及时间复杂度分析[EB/OL].https://blog.csdn.net/beautyofmath/article/details/48184331, 2015-09-02/2019-02-01.
[2] ECJTU_ACM_余伟伟.斐波那契数列算法及时间复杂度分析[EB/OL].https://blog.csdn.net/ecjtu_yuweiwei/article/details/47282457, 2015-08-04/2019-02-01.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值