大兔子生小兔子问题

来源:互联网 发布:linux root 密码 暴力 编辑:程序博客网 时间:2024/04/27 18:00

一、描述

        古典问题,有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第n个月后一共有多少对兔子?(兔子对数的规律为数列1,1,2,3,5,8,13,21....)

二、解答


public class Test {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 1; i <= 8; i++) {
System.out.println(getNum1(i) + "-" + getNum2(i));
}
// long result = getNum1(40);//递归40
long result = getNum2(40000000);//循环40000000
System.out.println(result);
System.out.println("总耗时:" + (System.currentTimeMillis() - start));
/**
* 测试第的兔子数量结果如下:
* 递归算法:月份 40, 总耗时:900 毫秒
* 循环算法:月份 40000000, 总耗时:70  毫秒
* 可得出在层数较大的时候,循环比递归有相当大的效率优势
*/
}

/**
* 分析:由于兔子不会死亡所以每月兔子比上月只增多不减少,本月兔子数量就是上月兔子数量+本月新增加的兔子数量
* 本月新增加的兔子来源于上月中可生产的兔子,可生产的兔子一只一次只生产一只新兔子,所以本月新增加的兔子数量=上月兔子里可生产的兔子数量
* 由于兔子出生后要过两个月才可以生产,所以上月兔子里可生产的兔子数量=两个月前兔子数量(两个月内生出的兔子还不能生产)
* 进而可知:本月兔子数量=上月兔子数量+上上月的兔子数量
* 到此就可以很容易想到使用递归算法实现
*/
//递归算法
static long getNum1(int month) {
if (month < 1) return 0;
if (month < 3) return 1;
return getNum1(month - 1) + getNum1(month - 2);
}
/**
* 递归算法虽然实现简单且易于理解,但效率却很低,由于每次计算都要将上次的计算再计算一遍并且还要再比上次多至少一次计算
* 这里将上次的计算再计算一次无疑是对CPU的浪费,由于我们不需要历史记录只需要使用上次的计算结果,所以可以将上次的结果做缓存
* 下次可以直接使用而无需再此计算,这样一来每次都只需一次计算,效率必然大幅度提高
*/
//循环算法
static long getNum2(int month) {
if (month < 1) return 0;
if (month < 3) return 1;

//初始化为i = 2即第二月的值
long pre2 = 1;//缓存上上月的兔子数量
long pre = 1;//缓存上月的兔子数量
long now = 1;//缓存本月的兔子数量
for (int i = 3; i <= month; i++) {//这里是在循环往月,所以不能等于<=month
now = pre2 + pre;//本月的兔子数量
pre2 = pre;//上月的数量将在下次循环时候变为上上月的数量
pre = now;//本月的数量将在下次循环时候变为上月的数量
}
return now;
}
}