需求:
有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,
假如兔子都不死,问指定月份的兔子对数为多少?
规律:
第一个月:1
第二个月:1
第三个月:2
第四个月:3
第五个月:5
第六个月:8
…
递归算法
上面是一个很简单的斐波那契额数列的例子。也是递归数列的经典例子。So,我们用递归来解决它
public class Test {
public static void main(String[] args){
int month = 6;
long num = Test.ribbit(month);//输入所求月份
System.out.println(month + "月后,兔子一共有"+num+"只");
}
private static long ribbit1 (int num){
if(num == 1 || num == 2){
return 1;
}
return ribbit(num-1)+ribbit(num-2);
}
}
啊~看起来很简单。
那么我们输入一个50看看他执行的时间,总数,与次数
但是代码要做简单的修改
public class Test {
static long cnt = 0;
public static void main(String[] args){
int month = 50;
long start = System.nanoTime();
long num = Test.ribbit(month);//输入所求月份
long end = System.nanoTime();
System.out.println(month + "月后,兔子一共有"+num+"只");
System.out.println("方法调用次数:" + cnt);
System.out.println("计算总的时间:" + (end - start));
}
private static long ribbit1 (int num){
cnt++;
if(num == 1 || num == 2){
return 1;
}
return ribbit(num-1)+ribbit(num-2);
}
}
其实就加上了一个计时器,与一个计数器(使用纳秒)
那么我们来看看结果
50月后,兔子一共有12586269025只
方法调用次数:25172538049
计算总的时间:47940560465
可以看到,递归用来算斐波那契数列真的是一点点的效率都没有。用了整整47秒。
所以就需要优化他了
备忘录算法
可以看到递归在调用方法的时候不停的在重复。那么有没有一种算法,可以将已经算过的值不用再算一次呢?比如我们如果求了第30个月的数量,那么在下一次调用的时候能不能直接调用呢?
似乎在JAVA里,HashMap可以很好的实现这一方法。
static HashMap<Integer,Long> map = new HashMap<Integer,Long>();
private static long ribbit02(int month) {
cnt++;
// 定义方法的结束条件
if (month == 1 || month == 2) {
return 1;
}
// 通过月份获取对应月份对数
Long num = map.get(month);
if (num == null) {
num = ribbit02(month - 1) + ribbit02(month -2);
map.put(month, num);
}
return num;
}
上面通过HashMap可以得到1-50月份所有的值,所以在之后调用的时候就不需要再次求值了。那么我们看看效率怎么样
50月后,兔子一共有12586269025只
方法调用次数:97
计算总的时间:585338
我们看到时间只用了0.5秒。方法只调用了97次。
已经很快了是不是。
动态规划
当然,神奇的算法工程师们当然不会止步于此。
只要我们每一次都几率上次和上上次的数量,不是就可以求出当月的数量了吗?
private static long ribbit03(int month) {
cnt++;
long a = 1; // 上个月的对数
long b = 1; // 上上个月的对象
long temp = 0; // 临时的变量,当前月份对数
for (int i = 3; i <= month; i++) {
temp = a + b;
b = a;
a = temp;
}
return temp;
来看看他的效率
50月后,兔子一共有12586269025只
方法调用次数:1
计算总的时间:3839
只用0.003秒了,相比于之前的40多秒,算法工程师们可以开开心心的下班回家了。
后记
当然除了动态规划外还有直接通过通项公式求和的方法
这种方法在这里就不做解释了。