有趣的算法-兔子序列


题目:

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

分析:

以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对兔子。

以此类推,可以得出如下关系:

当月兔子对数 = 上月兔子对数 + 上上月兔子对数。不难得出,该问题符合斐波那契数列规律。

所以很容易得出实现:

//算法1

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

但由于是递推实现,算法复杂度较高,感兴趣的可以自行计算一下,这里略过。

所以进行算法优化:

//算法2

int fib(int n)
{
    if(n<1)
        return -1;
    int *a=new int[n];
    a[1]=1;
    a[2]=1;
    for(int i=3; i<n; i++)
        a[i] = a[i-1] + a[i-2];
    return a[n];
}

该算法不再使用递归求解,而是使用数组进行存储中间变量,这样将算法的时间复杂度大大降低,从指数级降到了多项式级,当然也增加了空间复杂度。那还有没有更好的方式进行优化,既能降低时间复杂度,又不增加空间复杂度?

这里可以考虑迭代法,进行辗转相加法来实现:

//算法3

int fib(int n)
{
    int sum1, sum2;
    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;
}

此方法不再使用数组,而是使用辅助变量,将空间复杂度从O(n)降到了O(1)。

以上,同一个问题,稍加变换,得到的算法性能就大大不同,这正是算法之美所在。据了解,斐波那契数列的时间复杂度还可以从多项式级降到对数级,有兴趣的可以自行查阅资料。

posted @ 2021-06-16 14:12  雲淡風轻2020  阅读(585)  评论(0)    收藏  举报