您的位置:首页 > 其它

递归算法基础 斐波那契问题几种实现方法 两个经典问题(兔子问题、奶牛问题)

2014-06-08 11:27 337 查看
递归算法是一种直接或间接调用自身函数或方法的算法,实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。

递归与for循环区别:

递归:可看作到楼顶取东西,从一楼开始爬,看,不是楼顶,继续爬,每层楼看上去都一样,执行过程也一样,等你到楼顶了,取到了你想要的东西,然后一层一层的退回来。

循环:可看作驴拉磨,无论驴拉多少次,都是在原地打转,位置不变,变化的只是磨盘里的东西。

为什么要用递归:我们要解决的很多问题本身的定义就是“递归”的,比如最常见的“树的遍历”,树本身就是一个递归的结构,如果你想要用程序来完成对它的遍历,最自然的方式也就是递归地把这个遍历过程描述出来,试想一下如果你偏要用循环的方式来完成树的遍历得多麻烦?代码长度也会增长很多很多;所以面对递归的问题,递归的解法是最自然的、最好理解的。

1、经典问题——斐波那契数列之兔子问题

斐波那契数列:兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?

依次类推可以列出下表:

经过月数
0
1
2
3
4
5
6
7
8
9
10
11
12
幼仔对数
1
0
1
1
2
3
5
8
13
21
34
55
89
成兔对数
0
1
1
2
3
5
8
13
21
34
55
89
144
总体对数
1
1
2
3
5
8
13
21
34
55
89
144
233
幼仔对数=前月成兔对数 成兔对数=前月成兔对数+前月幼仔对数 总体对数=本月成兔对数+本月幼仔对数

前面相邻两项之和,构成了后一项。

2、扩展问题——奶牛问题

一个农夫养了一头牛,三年后,这头牛每年会生出1头牛,生出来的牛三年后,又可以每年生出一头牛……问农夫10年后有多少头牛?n年呢?(用JAVA实现)

2.1 用递归的方法 (结果为28)

public class Cow {
static int count = 1; //牛的数量
private static void feedCow(int year,int age){
year++; //第几年
age++; // 对应牛的年龄
if(year<=10){
if(age>=3){
count++;
feedCow(year,0); // 新生牛
}
feedCow(year,age); //老牛长一岁
}
}

public static void main(String[] args) {
new Cow().feedCow(0, 0);
System.out.println(count);
}
}


思路:今年的牛的数目=去年的牛的数目+三年前牛的数目 即:f(n)=f(n-1)+f(n-3)

这里 f(n-2) 第一年 、 f(n-1) 第二年、 f(n) 第三年

int Fibonacci(int n){
if (n==1 || n==2 || n==3){
 return 1;
 }
 return Fibonacci(n-1)+Fibonacci(n-3);
 }
扩展:m年则得到 f(n)=f(n-1)+f(n-m) 又假如每头牛的寿命只有十年,则 f(n)=f(n-1)+f(n-3)-f(n-10)

3、递归优化

缓存原理:用类变量保存已经求出来的斐波那契结果,内存中这个结果如果已经有了,那么可以直接返回,不用再次计算。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
* 递归求斐波那契数列
*/
public class Recursion {
public static void main(String[] args) {
// TODO Auto-generated method stub
BufferedReader strin=new BufferedReader(new InputStreamReader(System.in));
int n=0;
try {
System.out.println("请输入50以内数字:");
n=Integer.parseInt(strin.readLine());
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Fibonacci.fibonacci(n));
Fibonacci.printMap();
}

}
/**
* 工具类
* 添加了缓存控制
*/
class Fibonacci{
private static Map m=new HashMap();//用于保存n对应的斐波那契值
static long fibonacci(int n){
if(n<=1) return 1;
//缓存!如果存在直接返回
if(m.containsKey(n))
return (long)m.get(n);
long store=fibonacci(n-1)+fibonacci(n-2);
m.put(n, store);
return store;
}
/**
* 对m的读取可能破坏了封装
* 打印出map
*/
public static void printMap(){
Set keySet = m.keySet();
Iterator keys=keySet.iterator();
while(keys.hasNext()){
int temp=(int)keys.next();
System.out.println(temp+":"+m.get(temp));
}

}
}
4、斐波那契几种实现方法

4.1非尾递归实现(书本上经常出现)

public static long fibo1(long n){
if(n<2) return n;
return fibo1(n-1)+fibo1(n-2); //小心栈溢出
}


4.2 缓存实现

public static int LENGTH=30; //过大了就会占用过多空间
public static long[] cache=new long[LENGTH];
public static long fibo2(int n){
if(n<2) return n;
if(n>=LENGTH){
return fibo2(n-1)+fibo2(n-2);
}else if(cache
==0){
cache
=fibo2(n-1)+fibo2(n-2); //减少重复计算
}
return cache
;
}


4.3 迭代实现

public static long fibo3(long n){
if(n<2) return n;
long pre=1,prepre=1,ret=0;
for(int i=2;i<n;i++){
ret=pre+prepre;
prepre=pre;
pre=ret;
}
return ret;
}


4.4 尾递归实现

public static long fibo4(int n){
if(n<2) return n;
return fibo4Helper(n, 1, 1, 3); //保持与非尾递归接口不变,是借助帮助方法实现尾递归的
}
private static long fibo4Helper(int n,long prepre,long pre,int begin){
if(n==begin) return pre+prepre;
return fibo4Helper(n, pre, prepre+pre, ++begin);    //这里相当于迭代实现for-loop的浓缩
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐