递归算法基础 斐波那契问题几种实现方法 两个经典问题(兔子问题、奶牛问题)
2014-06-08 11:27
337 查看
递归算法是一种直接或间接调用自身函数或方法的算法,实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。
递归与for循环区别:
递归:可看作到楼顶取东西,从一楼开始爬,看,不是楼顶,继续爬,每层楼看上去都一样,执行过程也一样,等你到楼顶了,取到了你想要的东西,然后一层一层的退回来。
循环:可看作驴拉磨,无论驴拉多少次,都是在原地打转,位置不变,变化的只是磨盘里的东西。
为什么要用递归:我们要解决的很多问题本身的定义就是“递归”的,比如最常见的“树的遍历”,树本身就是一个递归的结构,如果你想要用程序来完成对它的遍历,最自然的方式也就是递归地把这个遍历过程描述出来,试想一下如果你偏要用循环的方式来完成树的遍历得多麻烦?代码长度也会增长很多很多;所以面对递归的问题,递归的解法是最自然的、最好理解的。
1、经典问题——斐波那契数列之兔子问题
斐波那契数列:兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?
依次类推可以列出下表:
幼仔对数=前月成兔对数 成兔对数=前月成兔对数+前月幼仔对数 总体对数=本月成兔对数+本月幼仔对数
前面相邻两项之和,构成了后一项。
2、扩展问题——奶牛问题
一个农夫养了一头牛,三年后,这头牛每年会生出1头牛,生出来的牛三年后,又可以每年生出一头牛……问农夫10年后有多少头牛?n年呢?(用JAVA实现)
2.1 用递归的方法 (结果为28)
思路:今年的牛的数目=去年的牛的数目+三年前牛的数目 即:f(n)=f(n-1)+f(n-3)
这里 f(n-2) 第一年 、 f(n-1) 第二年、 f(n) 第三年
3、递归优化
缓存原理:用类变量保存已经求出来的斐波那契结果,内存中这个结果如果已经有了,那么可以直接返回,不用再次计算。
4.1非尾递归实现(书本上经常出现)
4.2 缓存实现
4.3 迭代实现
4.4 尾递归实现
递归与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的浓缩 }
相关文章推荐
- java基础问题----java中有几种方法可以实现一个线
- java基础问题---java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用
- java基础问题---多线程有几种实现方法
- JAVA多线程经典问题 -- 生产者 消费者 同步队列实现方法
- android基础知识28:Android实现计时与倒计时的几种方法
- 递归算法与两个经典问题:汉诺塔问题和八皇后问题
- 任何国家都无法限制数字货币。为什么呢? 要想明白这个问题需要具备一点区块链的基础知识: 区块链使用的大致技术包括以下几种: a.点对点网络设计 b.加密技术应用 c.分布式算法的实现 d.数据存储技术 e.拜占庭算法 f.权益证明POW,POS,DPOS 原因一: 点对点网络设计 其中点对点的P2P网络是bittorent ,由于是点对点的网络,没有中心化,因此在全球分布式的网
- javascript图片与加载处理基础详细讲解几种方法实现
- 背包问题经典实现方法
- 回顾基础知识——实现阶乘计算的几种方法
- linux基础——经典线程同步问题解析及编程实现
- PHP开发中解决并发问题的几种实现方法分析
- 几种经典的二值化方法及其vb.net实现
- C++初学之 2.递归算法典型案例: 斐波那契(Fibonacci)兔子问题(第三项为前两项的累加问题)
- 由于设计页面需要,要把两个并排显示的 div 实现一样高的效果, n 行 n 列布局,每列高度(事先并不能确定哪列的高度)相同,每个设计师追求的目标。方法有以下几种: 1 JS 实现(判断 2 个 d
- 实现“两个变量的互换”的几种方法
- 【最近面试遇到的一些问题】多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么
- 几种经典排序算法的JS实现方法
- 几种排序方法的实现及链表的初始化问题
- Finonacci sequence,斐波那契,经典的兔子繁殖,更新为兔子会死,通项推导,循环,递归,dp实现