神奇的兔子序列

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

:兔子数列即斐波那契数列,它的发明者是意大利数学家列昂纳多•斐波那契(Leonardo Fibonacci,1170—1250)。1202年,他撰写了《算盘全书》(《Liber Abaci》)一书,该书是一部较全面的初等数学著作。书中系统地介绍了印度—阿拉伯数码及其演算法则,介绍了中国的“盈不足术”;引入了负数,并研究了一些简单的一次同余式组。

案例分析

以1对新出生小兔子为例。

第1个月:小兔子1没有繁殖能力,所以还是1对。

第2个月:小兔子1进入成熟期,还是1对。

第3个月:小兔子1生了1对小兔子2,本月共有2对兔子。

第4个月:小兔子1又生了1对小兔子3,本月共有3对兔子。

第5个月:小兔子1又生了1对小兔子4,而在第3个月出生的小兔子2也生了1对小兔子5,本月共有5对兔子。

第6个月:小兔子1,2,3各生1对小兔子,本月共有8对兔子。

。。。

用图片表示为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oNdpEFmT-1666346510480)(C:\Users\iDoit\Desktop\兔子.jpg)]

因此可以推出:
兔子序列递归python 兔子序列图_时间复杂度
其对应的斐波那契数列如下:
兔子序列递归python 兔子序列图_算法_02
其对应的递归表达式为
兔子序列递归python 兔子序列图_算法_03
根据递归公式,我们可以设计出其对应的递归算法:

//递归的方式
fib(int n)
{
  if(n < 1)
  {
      return -1;
  }
  if(n == 1 || n == 2)     //对应递归表达式中n=1或n=2的情况
  {
      return 1;
  }
  return fib(n -1) + fib(n - 2);  //对应n>2的情况
}

下面我们分析一下它的时间复杂度,斐波那契数列的通项公式为(其推到过程可自行百度)
兔子序列递归python 兔子序列图_时间复杂度_04
当n趋近于无穷的时候,根据其通项公式分析可得,上例算法的时间复杂度为兔子序列递归python 兔子序列图_兔子序列递归python_05,属于指数阶的算法。

根据递归公式设计出来的算法时间复杂度过大,影响实际应用的效率,我们不妨从式子(1)入手,从式子(1)可以发现:数列从第三项开始,数列中的每一项都是前两项之和,那我们的算法如果不采用迭代,直接使用数组,是否可以降低其时间复杂度。

//数组的方式
int fib(int n)
{
    if( n == 1 || n == 2)
    {
        return 1;
    }
    int pre1 = 1;   //记录前一项
    int pre2 = 1;   //记录前二项 
    for(int i = 3, i <= n; i++)
    {
        int tmp = pre2 + pre1;
        pre2 = pre1;
        pre1 = tmp;
    }
    return pre1;
}

通过上例,可以明显的发现时间算法执行得次数的高阶项只剩n,其复杂度变为兔子序列递归python 兔子序列图_算法_06,时间复杂度大大降低。

我们对斐波那契序列进一步分析,根据公式(2)引入矩阵,可以归纳出公式(4):
兔子序列递归python 兔子序列图_算法_07
其中兔子序列递归python 兔子序列图_递归_08(为了分析方便,我们引入兔子序列递归python 兔子序列图_递归_08),那么当n=1、2、3的时候会有
兔子序列递归python 兔子序列图_时间复杂度_10
兔子序列递归python 兔子序列图_兔子序列递归python_11

兔子序列递归python 兔子序列图_兔子序列递归python_12
根据上面的式子我们可以得到
兔子序列递归python 兔子序列图_算法_13
由于兔子序列递归python 兔子序列图_算法_14,结合向量兔子序列递归python 兔子序列图_递归_15,则有
兔子序列递归python 兔子序列图_兔子序列递归python_16

结合(6),我们可以假设
兔子序列递归python 兔子序列图_兔子序列递归python_17

我们令兔子序列递归python 兔子序列图_兔子序列递归python_18,则
兔子序列递归python 兔子序列图_递归_19
事实上,我们可以通过数学归纳法对(8)进行验证(验证过程可自行百度)我们的假设是对。那么我们可以基于(8)继续进行分析。

由(2)和(8)可得
兔子序列递归python 兔子序列图_递归_20
由(9)可得
兔子序列递归python 兔子序列图_算法_21

兔子序列递归python 兔子序列图_时间复杂度_22

因此,我们可以判断n的奇偶性,来使用不同的计算方式,对应的代码如下:

//奇偶的方式
int fib(int n)
{
    if(n == 1 || n == 2)
    {
        return 1;
    }
    int k = (n & 1) == 1 ? (n + 1) /2 : n / 2;
    return (n & 1) == 1 ? (fib(k) * fib(k) + fib(k - 1) * fib( k- 1)) 
                        : fib(k - 1)) : (2 * fib(k - 1) + fib(k)) * fib(k);
}

接下来我们分析一下该算法的时间复杂度,以兔子序列递归python 兔子序列图_时间复杂度_23为例。

fib(5)   
                     /         \
               fib(3)          fib(2)   
             /        \              
         fib(2)      fib(1)

通过上面兔子序列递归python 兔子序列图_时间复杂度_23的例子,我们可以看到该算法明显的降低了兔子序列递归python 兔子序列图_斐波那契数列_25的时间复杂度,其每次以n减半的效率迭代,因此其时间复杂度为兔子序列递归python 兔子序列图_兔子序列递归python_26