码迷,mamicode.com
首页 > 其他好文 > 详细

斐波拉契数列、楼梯问题、奶牛问题

时间:2015-07-31 13:15:41      阅读:172      评论:0      收藏:0      [点我收藏+]

标签:

斐波拉契数列:波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)[from 百度百科 http://baike.baidu.com/link?url=8LKtKTAllUGDMe610zIO0DAjS3CCeAOpXiCFvH_Y47_I_XDRgzyGcrzsodd1OHO726FJNPWkqzkQC7PIuGu_I_

问题1:

对于斐波拉契经典问题,我们都非常熟悉,通过递推公式F(n) = F(n - 1) + F(n -  2),我们可以在线性时间内求出第n项F(n),现在考虑斐波拉契的加强版,我们要求的项数n的范围为int范围内的非负整数,请设计一个高效算法,计算第n项F(n)。第一个斐波拉契数为F(0)  = 1。给定一个非负整数,请返回斐波拉契数列的第n项,为了防止溢出,请将结果Mod 1000000007。[from牛客网 ]

方法1:

最基础的方法是最简单的递归,会产生大量的重复运算,时间复杂度太大,不予推荐。

方法2:

经过推导发现:

当n为偶数时 F(n) =  F(n/2)^2 + F(n/2 - 1)^2

当n为奇数时 F(n) =  F(n/2)^2 + 2*F(n/2)*F(n/2 - 1)

由此可采用:

int Fibonacci(int n)
{
    long i, j;
        if (n == 0 || n == 1)
            return 1;
        i = Fibonacci(n/2);
        j = Fibonacci(n/2 - 1);
        if (n%2 == 0)
            return (i*i + j*j)%1000000007;
        else 
            return (2*i*j+ i*i)%1000000007;
}

此算法类似与二分查找,假如方法1的时间复杂度为N,方法2的时间复杂度可能为log2 N(此处不确定

虽然这种方法有所改善,但是仍不理想,重复计算过多。

注:此方法的i*i部分是大整数之间的乘法,数量巨大,消耗大量时间。

方法3:

斐波拉契经典问题还可以通过矩阵的方法(具体方法此处不列举)来解,有方法2相比,算是一种空间换时间的折中。

由于斐波拉契矩阵大致为a[2][2]={1,1,1,0},矩阵大整数间的运算有所减少,相比方法2也是一种提升。

对于n为偶数或为奇数的问题:

为避免大整数间乘法,根据矩阵乘法规律:

当m为偶数时:arr(m/2, a);, 再求矩阵a*a

当m为奇数时:arr(m/2, a);,再求矩阵a*a*{1 , 1, 1, 0}


/*
矩阵处理函数
*/
void arr(int m, long long (*d)[2])
        {
            long long a[2][2];
            long long e[2][2];
            long long c[2][2] = {1, 1, 1, 0};
            if (m == 1) {
                d[0][0] = 1;
                d[0][1] = 1;
                d[1][0] = 1;
                d[1][1] = 0;
                return;
            }
            arr(m/2, a);
            e[0][0] = (a[0][0]*a[0][0] + a[0][1]*a[1][0])%1000000007;
            e[0][1] = (a[0][0]*a[0][1] + a[0][1]*a[1][1])%1000000007;
            e[1][0] = (a[1][0]*a[0][0] + a[1][1]*a[1][0])%1000000007;
            e[1][1] = (a[1][0]*a[0][1] + a[1][1]*a[1][1])%1000000007;
            if (m%2 != 0) {
                d[0][0] = (e[0][0]*c[0][0] + e[0][1]*c[1][0])%1000000007;
                d[0][1] = (e[0][0]*c[0][1] + e[0][1]*c[1][1])%1000000007;
                d[1][0] = (e[1][0]*c[0][0] + e[1][1]*c[1][0])%1000000007;
                d[1][1] = (e[1][0]*c[0][1] + e[1][1]*c[1][1])%1000000007;
            }
            else {
            d[0][0] = e[0][0];
            d[0][1] = e[0][1];
            d[1][0] = e[1][0];
            d[1][1] = e[1][1];
            }
        }
/*
调用函数
*/
int Fibonacci(int n) {
        // write code here
        long long b[2][2] = {1, 1, 1, 0};
        if (n == 0 || n == 1)
            return 1;
        arr(n - 1, b);
        return (b[0][0] + b[0][1])%1000000007;
}


问题2:

现在有一栋高楼,但是电梯却出了故障,无奈的你只能走楼梯上楼,根据你的腿长,你一次能走1级或2级楼梯,已知你要走n级楼梯才能走到你的目的楼层,请计算你走到目的楼层的方案数,由于楼很高,所以n的范围为int范围内的正整数。

给定楼梯总数n,请返回方案数。为了防止溢出,请返回结果Mod 1000000007的值。[from 牛客网 ]

分析:

一个人一次只能走一个台阶,或者两个台阶。那么到台阶N总共有两种大方式,从N-1上来(N-1 > 0),从N-2(N-2 > 0)上来。那么到达台阶N的方式即到达N-1的方式加上到达N-2的方式,即F(n) = F(n-1) + F(n-2)。那么该问题即是斐波拉契数列问题,方法完全一样。


问题3:

在农场中,奶牛家族是一个非常庞大的家族,对于家族中的母牛,从它出生那年算起,第三年便能成熟,成熟的母牛每年可以生出一只小母牛。即母牛从出生 开始的第三年便能做妈妈。最开始农场只有一只母牛,它从第二年开始生小母牛。请设计一个高效算法,返回第n年的母牛总数,已知n的范围为int范围内的正 整数。

给定一个正整数n,请返回第n年的母牛总数,为了防止溢出,请将结果Mod 1000000007。[from 牛客网 ]

分析:

奶牛分为成熟和未成熟两种,成熟每年产子一枚,未成熟需三年成熟后才能产子。

将奶牛分为三类,成熟,出生一年,出生两年

第N年奶牛总数的 = 三种奶牛数想加

第N年 成熟的奶牛 = 第N-1年成熟的奶牛 + 第N-1年出生两年的奶牛

第N年 出生一年的奶牛 = 第N-1年成熟的奶牛

第N年 出生两年的奶牛 = 第N-1年出生一年的奶牛 = 第N-2年成熟的奶牛

那么:

全用成熟的奶牛代替出生一年或两年的奶牛:

第N年 成熟的奶牛 = 第N-1年成熟的奶牛 + 第N-3年成熟的奶牛

F(n) = F(n-1) + F(n-3)

第N年 出生一年的奶牛 = 第N-1年成熟的奶牛

第N年 出生两年的奶牛 = 第N-2年成熟的奶牛

第N年 奶牛总数 = 第N年成熟 + 第N-1年成熟的奶牛 + 第N-2年成熟的奶牛

此时得到了,奶牛总数和成熟奶牛之间的关系

由矩阵乘法得   

1    0    1            F(n-1)            F(n-1) + F(n-3)              F(n)

1    0    0     X     F(n-2)     =    F(n-1)                    =      F(n-1)

0    1    0            F(n-3)            F(n-2)                             F(n-2)



此时,经过矩阵乘法,F(n) 里的n顺利向后递增,则矩阵可满足此算法,最后的奶牛总数为:F(n) + F(n - 1) + F(n - 2)

此算法采用3*3矩阵,其他与问题2相似:

void arr(int m, long long (*d)[3])
        {
            long long a[3][3];
            long long e[3][3];
            long long c[3][3] = {1, 0, 1, 1, 0, 0, 0, 1, 0};
            if (m == 1) {
                d[0][0] = 1;
                d[0][1] = 0;
                d[0][2] = 1;
                d[1][0] = 1;
                d[1][1] = 0;
                d[1][2] = 0;
                d[2][0] = 0;
                d[2][1] = 1;
                d[2][2] = 0;
                return;
            }
            arr(m/2, a);
            e[0][0] = (a[0][0]*a[0][0] + a[0][1]*a[1][0] + a[0][2]*a[2][0])%1000000007;
            e[0][1] = (a[0][0]*a[0][1] + a[0][1]*a[1][1] + a[0][2]*a[2][1])%1000000007;
            e[0][2] = (a[0][0]*a[0][2] + a[0][1]*a[1][2] + a[0][2]*a[2][2])%1000000007;
            e[1][0] = (a[1][0]*a[0][0] + a[1][1]*a[1][0] + a[1][2]*a[2][0])%1000000007;
               e[1][1] = (a[1][0]*a[0][1] + a[1][1]*a[1][1] + a[1][2]*a[2][1])%1000000007;
            e[1][2] = (a[1][0]*a[0][2] + a[1][1]*a[1][2] + a[1][2]*a[2][2])%1000000007;
            e[2][0] = (a[2][0]*a[0][0] + a[2][1]*a[1][0] + a[2][2]*a[2][0])%1000000007;
            e[2][1] = (a[2][0]*a[0][1] + a[2][1]*a[1][1] + a[2][2]*a[2][1])%1000000007;
            e[2][2] = (a[2][0]*a[0][2] + a[2][1]*a[1][2] + a[2][2]*a[2][2])%1000000007;
            if (m%2 != 0) {
                d[0][0] = (e[0][0]*c[0][0] + e[0][1]*c[1][0] + e[0][2]*c[2][0])%1000000007;
                d[0][1] = (e[0][0]*c[0][1] + e[0][1]*c[1][1] + e[0][2]*c[2][1])%1000000007;
                d[0][2] = (e[0][0]*c[0][2] + e[0][1]*c[1][2] + e[0][2]*c[2][2])%1000000007;
            
            d[1][0] = (e[1][0]*c[0][0] + e[1][1]*c[1][0] + e[1][2]*c[2][0])%1000000007;
            d[1][1] = (e[1][0]*c[0][1] + e[1][1]*c[1][1] + e[1][2]*c[2][1])%1000000007;
            d[1][2] = (e[1][0]*c[0][2] + e[1][1]*c[1][2] + e[1][2]*c[2][2])%1000000007;
            
            d[2][0] = (e[2][0]*c[0][0] + e[2][1]*c[1][0] + e[2][2]*c[2][0])%1000000007;
                d[2][1] = (e[2][0]*c[0][1] + e[2][1]*c[1][1] + e[2][2]*c[2][1])%1000000007;
                d[2][2] = (e[2][0]*c[0][2] + e[2][1]*c[1][2] + e[2][2]*c[2][2])%1000000007;
            }
            else {
                d[0][0] = e[0][0];
                d[0][1] = e[0][1];
                d[0][2] = e[0][2];
            
            d[1][0] = e[1][0];
            d[1][1] = e[1][1];
            d[1][2] = e[1][2];
            
            d[2][0] = e[2][0];
               d[2][1] = e[2][1];
                d[2][2] = e[2][2];
            }
        }
    int countSum(int n) {
        // write code here
        long long b[3][3] = {1, 0, 1, 1, 0, 0, 0, 1, 0};
        int e[3] = {1, 1, 1}; //F(3) F(2) F(1)
        long long f[3];
        //前三个为满足条件直接输出
        if (n == 1)
            return 1;
        if (n == 2)
            return 2;
        if (n == 3 )
            return 3;
        arr(n - 3 , b);
        f[0] = b[0][0]*e[0] + b[0][1]*e[1] + b[0][2]*e[2];
        f[1] = b[1][0]*e[0] + b[1][1]*e[1] + b[1][2]*e[2];
        f[2] = b[2][0]*e[0] + b[2][1]*e[1] + b[2][2]*e[2];
        return (f[0] + f[1] + f[2]) %1000000007;
    }

总结:

奶牛问题与楼梯问题看似关系不大,最后都能转化为矩阵解法

矩阵能很好的简化多余运算,实现空间转时间。

斐波拉契数列、楼梯问题、奶牛问题

标签:

原文地址:http://my.oschina.net/u/2313065/blog/486032

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!