您的位置:首页 > 其它

斐波那契数列

2015-08-20 14:43 309 查看
编程之美有一道题是关于斐波那契数列,作者提供了三种解决思路:

第一种解法:

根据递推关系定义:




其转换成递归树,该结构如图:




从上面递归树结构可以看出其有重叠情况,故编程之美提出可以用一个存储变量来存储其计算过的值来排除解决重复的计算的问题,其时间复杂度为O(N),空间复杂度也为 O(N)。

第二种解法:

根据线性递推数列定义:具有形如xn+2=axn+1+bxn{{x}_{n+2}}=a{{x}_{n+1}}+{{b}_{xn}}的递推公式的数列叫做线性递推数列,其特征方程为:y2=ay+b{{y}_{2}}=ay+b ,再根据韦达定理可以得出其关系公式:xn=c1rn+c2sn{{x}_{n}}={{c}_{1}}{{r}^{n}}+{{c}_{2}}{{s}^{n}}其中r,s为其特征方程y2=ay+b{{y}_{2}}=ay+b的两个根,c1,c2可以根据x1x_1和x2x_2来求出.

斐波那契数列满足这条件,故可以求出其通项公式:f(n)=5√5×(1+5√2)n−5√5×(1−5√2)nf(n) = \frac{{\sqrt 5 }}{5} \times {\left( {\frac{{1 + \sqrt 5 }}{2}} \right)^n} - \frac{{\sqrt 5 }}{5} \times {\left( {\frac{{1 - \sqrt 5 }}{2}} \right)^n},尽管可以在O(1)的时间内得到f(n)但是由于出现了无理数,其精确度无法保障,故不是最好的选择。

第三种解法:

斐波那契数列是二阶递推数列,所以存在一个2*2的矩阵A,使得:

(fnfn−1)=(fn−1fn−2)∗A\left( {\begin{array}{*{20}{c}}{{f_n}}&{{f_{n - 1}}}\end{array}} \right) = \left( {\begin{array}{*{20}{c}}{{f_{n - 1}}}&{{f_{n - 2}}}\end{array}} \right)*A

求解可得:

A=(1110)A = \left( {\begin{array}{*{20}{c}}1&1\\1&0\end{array}} \right)

因此可以推导出:

(fnfn−1)=(fn−1fn−2)∗A=(fn−2fn−3)∗A2=....=(f1f0)∗An−1\left( {\begin{array}{*{20}{c}}{f_n}&{{f_{n - 1}}}\end{array}} \right) = \left( {\begin{array}{*{20}{c}}{{f_{n - 1}}}&{{f_{n - 2}}}\end{array}} \right)*A = \left( {\begin{array}{*{20}{c}}{{f_{n - 2}}}&{{f_{n - 3}}}\end{array}} \right)*{A^2} = .... = \left( {\begin{array}{*{20}{c}}{{f_1}}&{{f_0}}\end{array}} \right)*{A^{n - 1}}

问题就是求An−1A^{n-1},如果直接通过相乘来求的话,时间复杂度还是为O(n) ,计算机是用二进制来表达里面的数值的,既然这样,我们是不是可以用二进制来表示n呢?这样就可以把n拆成如下的公式:

n=ak∗2k+ak−1∗2k−1+⋯+a1∗2+a0n = {a_k}*{2^k} + {a_{k - 1}}*{2^{k - 1}} + \cdots + {a_1}*2 + {a_0} (其中aia_i=0或1,i=0,1,…,k);

故有得出以下公式:

An=Aak∗2k+ak−1∗2k−1+⋯+a1∗2+a0=(A2k)ak∗(A2k−1)ak−1∗⋯∗(A21)a1∗Aa0{A^n} = {A^{{a_k}*{2^k} + {a_{k - 1}}*{2^{k - 1}} + \cdots + {a_1}*2 + {a_0}}} = {({A^{{2^k}}})^{^{{a_k}}}}*{({A^{{2^{k - 1}}}})^{^{{a_{^{_{k - 1}}}}}}}* \cdots *{({A^{{2^1}}})^{^{{a_1}}}}*{A^{{a_0}}}

其中:A2i=A2i−1∗A2i−1{A^{{2^i}}} = {A^{{2^{i - 1}}}}*{A^{{2^{i - 1}}}}

那么时间复杂度就是 O(log2log_2n)次乘法了,不过还有计算矩阵相乘时间的复杂度,假设其为O(b),其中b为常量,与n无关,那么整个时间复杂度其实为O(b*log2log_2n),故当n大到一定程度时,其时间是优于第一种解法的。*

代码例子:

#include <iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
typedef long long LGLG;
typedef long long (*maxtrix_poniter)[2];
//单位矩阵
LGLG result[2][2]={1,0,0,1};
//辅助矩阵
LGLG A[2][2]={1,1,1,0};
//这里由于矩阵相乘元素不是很多,故采用了最普通矩阵相乘算法。
maxtrix_poniter maxtriPow(const maxtrix_poniter A,int n)
{
   //临时存储变量
   LGLG temp[2][2]={0};
   maxtrix_poniter m=A;
   for(;n;n>>=1)
   {
      //ai=1时才执行下面if语句
      if(n&1)
      {
         //A和存储结果矩阵相乘,得到n右移后丢下那些数据的值A的次方
         for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
            {
               int multtemp=0;
               for(int k=0;k<2;k++)
                  multtemp+=result[i][k]*m[k][j];
                  temp[i][j]=multtemp;
            }
          //拷贝数据
         for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
            {
               result[i][j]=temp[i][j];
            }
      }
      //两个A矩阵相乘得到下项A的值
      for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
            {
               int multtemp=0;
               for(int k=0;k<2;k++)
                  multtemp+=m[i][k]*m[k][j];
                  temp[i][j]=multtemp;
            }
      //拷贝数据
     for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
            {
               m[i][j]=temp[i][j];
            }
   }
   return result;
}
int Fibonacci(int n)
{
   if(n>=2)
   {
      maxtrix_poniter an=maxtriPow(A,n-1);
      return (1*an[0][0]);
   }
   else if(n==1)
   {
      return 1;
   }
   else
   {
      return 0;
   }
}
int main()
{

    cout << Fibonacci(9) << endl;
    return 0;
}


参考资料:http://wenku.baidu.com/link?url=-qLKtdiRHn_UFDU9nwCU_fuNvK0cKZlPA2UxaFmJ67MW23zzPFMG89L85IklodeZcXU9N9PDw-QsNyRLy1YNGq_xK8H23Rp4KcrjI_LavVm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: