Finonacci sequence,斐波那契,经典的兔子繁殖,更新为兔子会死,通项推导,循环,递归,dp实现

一,原问题,经典兔子繁殖问题。


数学家Leonardoda Fibonacci提出的兔子繁殖问题:

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


由此推出了著名的fibonacci数列。

递推公式为,F(n)=F(n-1)+F(n-2),n>=3。F(1)=F(2)=1;

还有通项公式,以及黄金分割等的讨论,请参考其他资料。


一句话理解此递推公式:

F(n)是当前月的兔子对数目,等于上个月已有的兔子对数(F(n-1)个),再加上新出生的兔子对数目(F(n-2))。上上个月的兔子对是F(n-2),每对兔子生一对新的兔子,所以新出生的兔子对为F(n-2)。

根据此公式,可以用循环、递归和dp三种方法来实现兔子的计数。

程序在最后给出,本文先将注意力放在问题改进上。

二,去掉“兔子不死”的假设。


我们很容易i注意到原兔子繁殖问题中有个很强的假设,就是“所有兔子都不死”,而实际的兔子是有寿命的,所以修订模型,假设兔子出生后第5个月就死去。


几句话理解修订后的兔子繁殖问题:

兔子分三类:新兔子不繁殖;老兔子会死掉;壮年兔子才有繁殖能力。

月龄分布:[0,2)月;[5,INF)月;[2,5)月。


先给出新问题的递推公式:

F(n) = F(n-1)+F(n-2)-F(n-4);n>=5

F(1)=1;F(2)=2;F(3)=3;F(4)=3;

推导过程请看下图:

图片第一行数字为“月份数”,从1月到16月。
第二行为经典兔子繁殖问题中,每个月的兔子总数。
图片最后两行数字,为经典问题和修订问题在每月兔子数目上面的对比,修订后的问题,去掉了“兔子永远不死”这个条件,每个月的兔子数目都比原问题中要少,月份越靠后,差距越大。
中间部分采用英文字母来给兔子编号,是我的主要推导过程,行与行,列与列都对齐了。
第一对兔子叫a,3月时生了b兔子,4月时生了c兔子,5月就死了。对应的地方有个X号,表示a兔子死去。a兔子对退出历史舞台的那个月,b兔子刚有繁殖能力,生了d兔子,cc兔子对还没有繁殖能力。
后面的兔子出生,兔子具备繁殖能力,兔子老死,依次类推。

延伸一下,修订后的兔子问题,更接近现实中的情况,就像人口演变的莱斯利模型。如果人类不死的话,莱斯利模型里面的人口增长趋近于fibnoacci数列。


三,java程序实现。


1,循环实现。

public static void main(String[] args){
	int f1 = 1;int f2 = 1;
	int n = 16;
	System.out.print("第"+n+"月的");
	while(--n>=2){
		int tmp = f2;
		f2 = f1+f2;
		f1 = tmp;
	}//while
	System.out.println("兔子数目为"+f2);
	return;
}//main

2,递归实现。

public static void main(String[] args){
		int n = 16;
		System.out.print("第"+n+"月的兔子数目为"+fb(n));
		return;
	}//main
	private static int fb(int n){
		if(n==1||n==2)return 1;
		return fb(n-1)+fb(n-2); 
	}//fb


3,dp实现。

public static void main(String[] args){
		int n = 16;
		int[] nums = new int[n+1];
		nums[1] = nums[2] = 1;
		for(int i = 3;i<=n;i++){
			nums[i] = nums[i-1] + nums[i-2];
		}//for i
		System.out.print("第"+n+"月的兔子数目为"+nums[n]);
		return;
	}//main

4,修订问题的dp解法
增加第5个月后会死。

package cn.edu.dodi.test;

public class Lianxi {
	public static void main(String[] args){
		int n = 16;
		int[] nums = new int[n+1];
		nums[1] = 1;nums[2] = 2;nums[3]=3;nums[4]=3;
		for(int i = 5;i<=n;i++){
			nums[i] = nums[i-1] + nums[i-2] - nums[i-4];
		}//for i
		System.out.print("第"+n+"月的兔子数目为"+nums[10]);
		return;
	}//main
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值