关于 斐波那契数列 的引申

来源:互联网 发布:java重载的条件 编辑:程序博客网 时间:2024/04/29 15:08

最基本的函数模型是:

          /  0                             n=0
f(n)=      1                             n=1
          \ f(n-1)+(f-2)              n>1


1.我们一般的解法是:

int fun( int n ){              if ( 0 == n )        {               return0;        }        elseif ( 1 == n )        {               return1;        }        else        {               returnfun(n - 1) + fun(n - 2);        }}



2.但是递归在大的数据的情况下可能超时( 特别是在OJ上的时候 )

那么我们想想还可以用:

#include <stdio.h> int main(){   long long f[51] = { 0, 1 };   long long n, a0 = 0, a1 = 1;   int i;    for( i = 2; i <= 50 ; i++ )   {            f[i] = a0 + a1;    // 此处只要遍历一次就ok            a0 = a1;            a1 = f[i];   }   while( scanf("%d", &n) != EOF )   // 我们可以得到任意的n的 Fibonacci 结果   {           printf("%lld\n", f[n]);   }   return 0;}


3.算法导论上的Naive recursive squaring method 方法:其实也就是将其化成数学问题解决!

   Fibonacci的一条性质是:求N项的公式是有的,即:黄金比Q的N次方/根号5 即: pow( Q, N )/sqrt( 5 )
                                                   得到最近的一个整数,那么就是第N项的 Fibonacci 数值! ( Q = ( sqrt(5) + 1 ) / 2 是黄金比  )

   那么求前15项的代码如下:

    
#include <stdio.h>#include <math.h>int main(){    int i;    double Q = 1.0 * ( 1 + sqrt( 5 ) ) / 2.0, t;        for( i = 1; i < 15; i++ )    {         t = pow( Q, i )/sqrt(5);   // 取最近的整数就是第你项的fibonacci元素                   if( (t - (int)t) < ( ( (int)t + 1 ) - t ) )  // 这个if是判断到底是那个整数近( 例如:1.3 离1近呢还是离2近 )         {              printf("%d ", int(t));         }         else         {             printf("%d ", int(t) + 1);         }    }    getchar();    return 0;}

实际我们知道,由于计算机的浮点数的位置限制,那么我们在做上面的算式时候,当N很大的时候,很多的位可能会丢失,那么所求的结果就是不准确的,所以理论上的意义会更大!实际编程时候,当N很大的时候实不可取的~

那么下面的办法就很重要了~

  

4.此处还要介绍一个非常好的办法:利用矩阵

Fibonacci 说白了就是一个递推序列,f( n ) = f( n - 1 ) + f( n - 2 );

变形一下有: f( n )  =  1 *f( n - 1 )  + 1 *  f( n - 2 );

那么使用矩阵表示就是:( 注意:下面表示的不好看,其实是矩阵表示,见谅! )

|  f( n )     |        |  1 1  |          |  f( n-1 ) |

|  f( n-1 )  |   =   |  1 0  |     *    |  f( n-2 )  |  

即:

|  f( n )     |     |  1 1  |                   |  f( 1 )  |

|  f( n-1 ) |  = |  1 0  | ^( n-1 )  *  |  f( 0 )  |

就是那个| 1 1 |

                | 1 0 |    的n-1次方后 乘以 初始化的两个数就是!


#include <stdio.h> long long all[71]; void fun( int n ){    int i;    long long mat[2][2] = { 1, 0, 0, 1 };      // 单位矩阵    long long tmp[2][2];       for( i = 2; i <= n; i++ )    {         tmp[0][0] = mat[0][0];         tmp[0][1] = mat[0][1];         tmp[1][0] = mat[1][0];         tmp[1][1] = mat[1][1];                 mat[0][0] = tmp[0][0] + tmp[0][1];         mat[0][1] = tmp[0][0];         mat[1][0] = tmp[1][0] + tmp[1][1];         mat[1][1] = tmp[1][0];                 all[i] = mat[0][0] * all[1] +mat[0][1] * all[0];    }} int main(){    int n;     all[0] = 0;    all[1] = 1;    fun( 70 );     while( scanf("%d", &n) != EOF)    {           if( n == 0  )           {               printf("0\n");           }           else if( n == 1 )          {               printf("1\n");           }           else           {               printf("%lld\n",all[n]);           }    }       return 0;}


关于 Fibonacci  的变体:

1》青蛙跳台阶问题:

      一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

分析:

    当n =1, 只有1中跳法;当n =2时,有2种跳法;当n = 3 时,有3种跳法;当n = 4时,有5种跳法;当n = 5时,有8种跳法

    所以还是Fibonacci 序列,只不过初始化为:f(1) = 1; f(2) = 2;

#include <stdio.h> long long all[71]; void fun( int n ){    int i;    long long mat[2][2] = { 1, 0, 0, 1 };      // 单位矩阵    long long tmp[2][2];       for( i = 3; i <= n; i++ )    {         tmp[0][0] = mat[0][0];         tmp[0][1] = mat[0][1];         tmp[1][0] = mat[1][0];         tmp[1][1] = mat[1][1];                 mat[0][0] = tmp[0][0] + tmp[0][1];         mat[0][1] = tmp[0][0];         mat[1][0] = tmp[1][0] + tmp[1][1];         mat[1][1] = tmp[1][0];                 all[i] = mat[0][0] * all[2] +mat[0][1] * all[1];    }} int main(){    int n;     all[0] = 0;    all[1] = 1;    all[2] = 2;    fun( 70 );     while( scanf("%d", &n) != EOF)    {           if( n == 0 || n == 1 )           {               printf("1\n");           }           else           {               printf("%lld\n",all[n]);           }    }       return 0;}

或者代码为:

( 下面的代码计算大的数的时候可能超时,不建议 )

#include <stdio.h> int count; void fun( int s, int i, int n ){          s += i;           if( s == n )      {          count++;          return;      }      else if( s > n )      {           return;      }      else        // 此处分叉      {            fun( s, 1, n );            fun( s, 2, n );      }} int main(){    int n;       while( scanf("%d", &n) != EOF)    {           count = 0;           fun( 0, 0, n );                     printf("%d\n", count);    }       return 0;}


2》 跳台阶问题(变态跳台阶)

    一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法

分析:

 

             / 1                        n=1
f(n)=   2                             n=2
          \ f(n-1)+(f-2)            n>2

 #include <stdio.h> int main(){   long long f[51] = { 0, 1, 2 };   long long n = 3, sum = 3;   int i;    for( i = 3; i <= 50 ; i++ )   {           f[i] = sum + 1;           sum += f[i];   }    while( scanf("%d", &n) != EOF )   {          printf("%lld\n", f[n]);   }    return 0;}


3》 矩形覆盖

      我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

分析:

      同上!( 说到底还是其变形 )


#include <iostream> using namespace std;   int main() {     long long f[71];     int i;     int n;       f[0] = 1;     f[1] = 1;           for(  i = 2; i <= 70; i++ )     {         f[i] = f[i-1] + f[i-2];     }       while( cin >> n )     {         cout << f[n] << endl;     }          return 0; }



原创粉丝点击