美文网首页
Fibonacci数列

Fibonacci数列

作者: dequal | 来源:发表于2017-05-21 16:31 被阅读0次

    Fibonacci数列

    Fibonacci数列是一个非常美丽、和谐的数列,有人说它起源于一对繁殖力惊人、基因非常优秀的兔子,也有人说远古时期的鹦鹉就知道这个规律。
    每一个学理工科的学生都知道Fibonacci数列,Fibonacci数列由如下递推关系式定义:

    F(0)=0, F(1)=1, n>1时,F(n)=F(n-1)+F(n-2)。
    

    每一个上过算法课的同学都能用递归的方法求解Fibonacci数列的第n+1项的值,即F(n),以下为相关算法的代码:

    int Fibonacci (int n)
    {
        if (n <= 0) return 0;
        else if (n == 1) return 1;
        else return Fibonacci(n-1) + Fibonacci(n-2);
      }
    

    那么问题来了,对于以上这种算法,能否进一步优化?

    分析与解法

    解法一:递推关系式的优化

    上面列出的算法是根据递推关系式的定义直接得出的,它在计算F(n)时,需要计算从F(2)到F(n-1)每一项的值,这样简单的递归式存在着很多的重复计算,如F(5) = F(4) + F(3) , 在求F(4)的时候也需要计算一次F(3)的大小...等等。请问这个算法的时间复杂度是多少?
    那么经过分析我们可以知道优化此类算法的重点在于减少重复计算。那么我们可以用一个数组存储所有已计算过的项。这样就可以达到用空间换取时间的目的。在这种情况下,时间复杂度为 O(N),而空间复杂度也为O(N)。
    那么有更快的算法吗?

    解法二:求解通项公式

    如果我们知道一个数列的通项公式,使用公式来计算就会更加容易了。那么问题的关键就是能不能把这个函数的递推公式计算出来。
    由递推公式: F(n)=F(n-1)+F(n-2),知道F(n)的特征方程为:
    x² = x + 1 , 有两个特征根x1,x2。
    则通项为F(n)= A x1 n + B x2 n( n次方,本人对markDown语法不是很熟练所以在此加以说明希望不要误导到读者,以上公式为:A倍 x1 的n次方) ,其中A,B可以通过F(0)和F(1)计算出来。

    解法三:分治策略

    因为Fibonacci数列是二阶递推数列,所以存在2*2的矩阵A,使得:

        [Fn Fn-1] = [Fn-1, Fn-2]*A
      通过递推可以求得A={{1, 1}{1, 0}}
      且:[Fn Fn-1] = [Fn-1, Fn-2]*A = [Fn-2, Fn-3]*A2= ... = [F1, F0]*An-1
    

    剩下的问题就是求解矩阵A的方幂。
    参考代码如下:

    1 #include <iostream>
     2 using namespace std; 
     3 
     4 typedef long long LL; 
     5 const int maxn = 2; 
     6 const int MOD = 100000007; 
     7 
     8 struct Matrix 
     9 {
    10     LL m[maxn][maxn]; 
    11 }; 
    12 
    13 Matrix A = {1, 1, 1, 0}; 
    14 Matrix I = {1, 0, 0, 1}; 
    15 
    16 Matrix multi(Matrix a, Matrix b)
    17 {
    18     Matrix c; 
    19     for (int i = 0; i < maxn; i++)
    20     {
    21         for (int j = 0; j < maxn; j++)
    22         {
    23             c.m[i][j] = 0; 
    24             for (int k = 0; k < maxn; k++)
    25             {
    26                 c.m[i][j] += a.m[i][k] * b.m[k][j] % MOD; 
    27             }
    28             c.m[i][j] %= MOD; 
    29         }
    30     }
    31     return c; 
    32 }
    33 
    34 Matrix power(Matrix A, int k)
    35 {
    36     Matrix ans = I, p = A; 
    37     while (k)
    38     {
    39         if (k & 1)
    40         {
    41             ans = multi(ans, p); 
    42             k--; 
    43         }
    44         k >>= 1; 
    45         p = multi(p, p); 
    46     }
    47     return ans; 
    48 }
    49 
    50 int main(int argc, char *argv[])
    51 {
    52     int n; 
    53     while (cin >> n)
    54     {
    55         if (n <= 0) 
    56         {
    57             cout << 0 << endl;
    58             continue; 
    59         }
    60         Matrix ans = power(A, n-1); 
    61         cout << ans.m[0][0] << endl;
    62     }
    63 }
    

    拓展

    假设A(0)=1,A(1)=2,A(2)=2。对于n>2,都有A(k)=A(k-1)+A(k-2)+A(k-3)。```
    
    (1) 对于任何一个给定的n,如何计算A(n)?
    
    (2) 对于n非常大的情况,如n=260的时候,如何计算A(n)modM(M<100000)呢?```
    
     解答:
    

    (1) [A(k) A(k-1) A(k-2)] = [A(k-1) A(k-2) A(k-3)]*B

    其中B={{1, 1, 0}, {1, 0, 1}, {1, 0, 0}}。

    (2)这个问题笔者思路如下,但并未通过代码证实:

    A(n)modM取值为0,1,2,...,M-1,为有限的,因此,三元组[A(k) A(k-1) A(k-2)]共有M3种可能。

    因此,A(n)modM是周期数列,而且,周期最大为M3。

    可以先求出周期T,然后再求A[260]```

    相关文章

      网友评论

          本文标题:Fibonacci数列

          本文链接:https://www.haomeiwen.com/subject/lvtvxxtx.html