【算法】兔子繁殖之斐波那契数列


整理一下经典算法用C/C++实现,并思考总结

开拓大脑的算法

题目:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?

常规解法

读题之后,我直接着手进行解题:

思路:设置2个变量分别保存成年兔子数,幼年兔子数量,设置一个计数器分组分别计算每对小兔子还有几个月成年,成年后加入成年兔子。成年兔子每月产下幼年兔子加入幼年兔子。

void rabitNormal(int month){
    const int MAX_SHALL_RABIT = 1000;//最大小兔子数量(这个算法用数组做有局限性,最大只能MAX_SMALL_RABIT个兔子)
    int big_rabits = 0;//大兔子数量(初始都是小兔子)
    int small_r = 1;//小兔子数量
    short small_rabits[MAX_SHALL_RABIT];
    small_rabits[0]=2;//初始化第一对小兔子计数器
    int idx = 1;
    for (int i=0; i<month; i++) {//每个月更新
        for (int j=0; j<idx; j++) {
            if(small_rabits[j]>0){
                small_rabits[j]--;
                if(small_rabits[j]==0){//第三个月
                    big_rabits ++;//大兔子增加
                    small_r--;//小兔子减少
                }
            }
        }
        for(int j=0;j<big_rabits;j++){
            small_rabits[idx++] = 2;//每对大兔子生一对小兔子
            small_r++;//小兔子数量增加
        }
    }
    cout<<big_rabits+small_r<<endl;//打印大兔子小兔子总和
}

这个方法直接从实现角度来解决这个问题,就事论事,不太优雅。

递归算法

因为每对兔子从出生到生产的过程是一致的,所以我考虑是不是封装一个方法来表示兔子的这一过程,然后用递归实现

//递归实现 返回第一对兔子和之后所有小兔子生下的总兔子对数
int rabits(int month){
    int r = 0;
    for(int i=2;i<=month;i++){
        r+=rabits(month-i);//从第3个月起每个月生一堆兔子
    }
    return 1 + r;//这只兔子升的小兔子的总对数
}

这个方法我认为从解决问题的角度不失为一个好方法。但对规律问题考虑不深入,也属于就事论事。

斐波那契数列

网上看了正规解法后发现,这道题表现的本质就是斐波那契数列 f(0)=0,f(1)=1...f(n)=f(n-1)+f(n-2) (当前项等于前两项之和)。这一数列强大之处我还无法深入体会,我就整理下如何让兔子生产和斐波那契数列联系起来:

思考过程
1.第一个月1对幼年兔子,0对成年兔子
2.第二个月把幼年兔子加入成年兔子(因为第3个月生产出的幼年兔子等于当月的成年兔子),0对幼年,1对成年
3.第三个月幼年兔子对数 = 前月成年兔子对数1,成年兔对数 = 前月幼兔对数+前月成年对数=0+1=1
总结公式:
兔子总对数 = 成年兔子对数+幼年兔子对数
成年兔子对数 = 前月成年兔子对数+前月幼年兔子对数 = 前月总对数
幼年兔子对数 = 前月成年兔子对数 = 大前月兔子总对数(由上条公式得到)
最终推导公式:兔子总对数 = 前月兔子总对数+大前月兔子总对数! 这个公式正好符合Fibonacci数列

void rabitFibonacci(){
    long f1,f2;
    int i;
    f1=f2=1;
    for(i=1;i<=20;i++)
    {
        printf("%12ld %12ld",f1,f2);
            if(i%2==0) printf("\n");/*控制输出,每行四个*/
            f1=f1+f2;/*前两个月加起来赋值给第三个月*/
            f2=f1+f2;/*前两个月加起来赋值给第三个月*/
    }
}

整个推导过程为了让幼年兔子对数更纯净(保证都是上月生产的兔子),次月我们就把前一个月的幼年兔子并入成年了,这是由于从客观上来说

  1. 当月成年对数决定次月幼年对数同时和当月幼年对数一起决定当月总对数
  2. 当月幼年对数和当月成年对数决定次月成年对数

所以以上对幼年对数当月的“纯净”处理有利于对幼年对数的直接使用(试想一下如果幼年对数中混有新生的和上个月生的,你该如何处理它)。
从这推导过程的构架和实际推导过程中,我认为变量定义功能的明确性和纯净性非常重要我要的不是这个数列和公式的直接应用,而是这个推导过程。想想当三个月变成四个月,变成五个月该怎么来推导?

小结:3个算法前2个算法是我自己写,第3个方法是参考网上的,推导过程是自己模拟的,虽然花了不少时间做推导,但我觉得还是值得的,因为我现在最缺的就是一颗万事静心思考本质的心,推导的方式固然重要,但这颗探究本质的心更重要

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容