算法 —— 递推 ——五种典型的递推关系之Fabonacci数列

在所有的递推关系中,斐波那契数列应该是最为熟悉的。

在最基础的程序设计语言Logo语言中,就有很多这类的题目。

而在较为复杂的Basic、Pascal、C语言中,斐波那契数列类的题目因为解法的相对容易一些,逐渐退出了竞赛的舞台。

可是这不等于说斐波那契数列没有研究价值。

Fabonacci数列常出现在比较简单的组合计数问题中。

在优化法中,Fabonacci数列的用处也得到了较好的体现。

兔子数列

在这本书中,斐波那契提出了一个问题:

在第一个月有一对刚出生的小兔子,在第二个月小兔子变成大兔子并开始怀孕,第三个月大兔子会生下一对小兔子,并且以后每个月都会生下一对小兔子。 如果每对兔子都经历这样的出生、成熟、生育的过程,并且兔子永远不死,那么兔子的总数是如何变化的?

我们不妨先来看个图:
在这里插入图片描述

第一个月只有一对兔宝宝,1对兔子。

第二个月兔宝宝变成大兔子,1对兔子。

第三个月大兔子生了一对兔宝宝,一大一小2对兔子。

第四个月大兔子继续生一对兔宝宝,小兔子变成大兔子。两大一小3对兔子。

….

我们把这个数列列表

在这里插入图片描述
我们发现会发现以下几个规律:

前一个月的大兔子对数就是下一个月的小兔子对数。
前一个月的大兔子和小兔子对数的和就是下个月大兔子的对数。

按照这个表格,我们会发现无论是小兔子对数、大兔子对数还是总对数,除了最初几个数字不一样之外,后面都是按照1、1、2、3、5、8、13…变化的,这个数列就称为兔子数列或者斐波那契数列。

兔子数列最大的特点就是前两项之和等于后一项,比如1+1=2、1+2=3、2+3=5、3+5=8、5+8=13…

我们用an表示一个数列的第n项,那么斐波那契数列的规律就是

在这里插入图片描述
这种式子称为递推式,也就是说可以从前面一项或几项,计算出后面一项的式子。再结合前两项a1=a2=1,就可以得到后面任意一项了。

神奇的数列

也许许多人觉得,斐波那契数列不过是浩如烟海的数学海洋中的一滴水。但是实际上,从这个数列被提出的那一天起,几百年来人们在许多领域都发现了它的影子。

在数学上,许多求“方法数”的问题,答案都是斐波那契数列。例如:如果我们要上一个N级台阶的楼梯,每次只能走1格或者2格,那么一共有多少种走法呢?

如果只有一级台阶,显然只有1种走法。

如果有两级台阶,显然可以走一步,也可以走两步,因此有2种走法。

如果有三级台阶,就有如图所示的3种走法。

在这里插入图片描述
1、2、3这三个数字都是斐波那契数。那么,如果有更多台阶怎么办呢?这就需要递推式了。

由于一步最多走连两个台阶,因此要到达第N级台阶,有两种方案:

走到第N-1级台阶上,然后走1级台阶跨到最上方;
走到第N-2级台阶上,然后一步走两级台阶跨到最上方。

注意,从第N-2级台阶走1级到N-1级台阶这种情况已经在第一种情况中计算过了。

用a(N-1)和a(N-2)分别表示走到第N-1级和第N-2级台阶的方法数,那么走到第N级台阶的方法数就是:

aN= a(N-1)+ a(N-2)

显然,这就是斐波那契数列的递推公式,因此走台阶问题的解刚好是斐波那契数列。

生活中最典型的斐波那契数列应用是在植物学中。
在这里插入图片描述
大树在生长的过程中会长出分枝,如果我们从下到上数分枝个数,就会发现依次是1、1、2、3、5、8、13…等等,刚好是斐波那契数列。

有科学家对这种现象的解释是与兔子繁殖后代相同:每过一段时间老树枝都会萌发新芽,而新芽成长为成熟的树枝后也会每隔一段时间萌发一次新芽。

另一个神奇的例子就是向日葵等植物。

在这里插入图片描述
如果我们仔细观察,就会发现向日葵盘内的种子形成两组螺旋线,一组是顺时针的,另一组是逆时针的。

而这两组螺旋线的条数刚好是两个相邻的斐波那契数,小向日葵是34和55,大向日葵是144和233。

松果种子、菜花表面也有类似的规律。
在这里插入图片描述

有科学家认为:这种排列可以使得种子的堆积最密集,最有利于植物繁衍后代。

八百年来,人们在各个领域都发现了斐波那契数列。

尤其是十九世纪开始,人们发现了斐波那契数列在计算机、物理、化学等领域的应用,这个古老的数列焕发了新的青春。

1963年,斐波那契协会成立,并出版了《斐波那契季刊》用以刊登与斐波那契数列相关的研究成果。

代码实现

基本循环算法

# include <iostream>
# include <cstdio>
using namespace std;
int main(){
    int a=0,b=1,c=1,n;
    cin>>n;//输入n
    for(int i=3;i<=n;i++){
        a=b;
        b=c;
        c=a+b;
    }
    cout<<c; //输出最终结果 
    return 0;
}

递归算法实现

# include <iostream>
# include <cstdio>
using namespace std;
int f(int n){
    if(n==0) return 0;
    if(n==1) return 1;
    if(n>=2){
        return f(n-1)+f(n-2);
    }
}
int main(){
    int n;
    cin>>n;
    printf("%d",f(n));
    return 0;
}

递归算法优化(记忆化搜索)

# include <iostream>
# include <cstdio>
# define MAX (101)
using namespace std;
int a[MAX];
int f(int n){
    if(a[n]!=-1) return a[n];
    else{
        a[n]=f(n-1)+f(n-2);
        return a[n];
    }
}
int main(){
    int n;
    cin>>n;
    for(int i=0;i<=MAX-1;i++){        //初始化 
        a[i]=-1;
    }
    a[0]=0;a[1]=1;
    printf("%d",f(n));
    return 0;
}

高精度计算

#include<iostream>
#include<cstring>
using namespace std;
char sum[1200];
int s=0,m=0,n;
int main()
{
 	cin>>n;
    string s1,s2;
    int a[1200],b[1200],he,i;
    s1="0";s2="1";
    for(m=2;m<n;m++)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        a[0]=s1.length();
        for(i=1;i<=a[0];i++)
            a[i]=s1[a[0]-i]-'0';
        b[0]=s2.length();
        for(i=1;i<=b[0];i++)
            b[i]=s2[b[0]-i]-'0';
        he=(a[0]>b[0]?a[0]:b[0]);
        for(i=1;i<=he;i++)
        {
            a[i]+=b[i];
            a[i+1]+=a[i]/10;
            a[i]%=10;
        }
        he++;
        while((a[he]==0)&&(he>1))he--;
           for(i=he,s=0;i>=1;i--,s++)
               sum[s]=a[i]+'0';
        s1=s2;
        s2=sum;
    }
    cout<<s2<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/AlexKing007/p/12338927.html