java实现斐波那契数列的3种方法
对于优秀的算法设计员来说,在程序功能主体实现的基础上无非关心两个东西,一个设计算法的时间复杂度,一个是空间复杂度(说白了就是执行一个程序所用的时间和占用的内存空间);在根据不同的应用场景的基础上,一般充满智慧的算法设计师会在时间和空间两个相对矛盾的资源中寻求到平衡点,如实时性要求高的系统一般会以空间资源换取时间或者对于常用到的对象一般会常驻内存以提高响应时间(缓存技术和现在比较流行NoSQL中大多是内存数据库都是如此),对于内存资源比较宝贵的嵌入式系统而言一般会以时间上的延迟来换取时间。
下面从费波那西数列三个实现上来说说,怎么才能真正设计出真正符合实际应用场景的优秀算法
首先说说费波那西数列:
从文字上说,费波那西数列由0和1开始,之后的费波那西系数就由之前的两数相加,数列形式如下:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,………………
在数学上,是以递归的方法来定义:
F_0=0
F_1=1
F_n = F_{n-1}+ F_{n-2}
实现需求:输入序号n返回得到对应费波那西数
程序实现1——函数自迭代
/** * 函数自迭代 * @Title: fnType1 * @Description: TODO * @param @param n * @param @return * @return int * @throws Exception */ public int fnType1(int n)throws Exception{ if(n==0){ return 0; }else if(n==1||n==2){ return 1; }else if(n>2){ int temp=fnType1(n-1)+fnType1(n-2); if(temp<0){ throw new Exception("Invalid value for int type, too larage"); }else{ return temp; } }else{ throw new Exception("IllegalArgument value for n,please enter n>=0 "); } }
此种方式缺点:大量迭代不断消耗栈空间(搞web开发调试维护的都应该知道服务器栈资源的可贵,如果大量并发调用迭代导致服务器栈资源迟迟得不到回收,而导致web服务器崩溃),效率底,函数自闭性比较弱(优秀的接口应该对输入输出可能出现的错误信息进行捕捉,并提供清楚明了的处理结果),很容易出现错误,调试困难,实际应用中一般不建议使用这种方式,使用时迭代次数也不能超过3次;
程序实现2——时间换空间
/** * 时间换空间 * @Title: fnType2 * @Description: TODO * @param @param n * @param @return * @return int (n<0 return -1,beyond max int size return -2) * @throws */ public int fnType2(int n){ int result=-1; int temp1=0; int temp2=1; for(int index=0;index<=n;index++){ if(index==0){ result=temp1; }else if(index==1){ result=temp2; }else{ result=temp1+temp2; if(result<0){ result=-2; break; } temp1=temp2; temp2=result; } } return result; }
此方法主要使用于:使用场景一:对于对象或变量使用次数比较少,使用一次以后就不会再使用的场景;使用场景二:对于内存资源比较稀缺的实时性要求不是太高的嵌入式系统设计中多会采用此种方式;
程序实现3——空间换取时间
private static List<Integer> fnData=new ArrayList<Integer>(); private static final int maxSize=50000; /** * 初始化器 * @Title: setFnData * @Description: TODO * @param * @return void * @throws */ private static void setFnData(){ int result=-1; int temp1=0; int temp2=1; for(int index=0;index<=maxSize;index++){ if(index==0){ result=temp1; }else if(index==1){ result=temp2; }else{ result=temp1+temp2; if(result<0){ result=-2; break; } temp1=temp2; temp2=result; } fnData.add(result); } } /** * 对外接口 * @Title: getFnData * @Description: TODO * @param @param n * @param @return * @return int <span style="font-family: sans-serif;">(n beyond fnData.size() and n<0 return -1)</span> * @throws */ public int getFnData(int n){ if(fnData.size()==0){ setFnData(); } if(fnData.size()>n&&n>=0){ return fnData.get(n); }else{ return -1; } }
此方法一般用于:对象或变量在程序运行的整个生命周期都存在或频繁调用的场景,如调用外部WebService接口、抽象持续化层、常用配置文件参数加载等等
测试用例:
package com.dbc.yangg.swing.test; import java.util.ArrayList; import java.util.List; /** * 输入序号n返回得到对应费波那西数 * @ClassName: Init * @Description: TODO * @author guoyang2011@gmail.com * @date 2014年1月10日 下午7:52:13 * */ public class Init { /** * 函数自迭代 * @Title: fnType1 * @Description: TODO * @param @param n * @param @return * @return int * @throws Exception */ public int fnType1(int n)throws Exception{ if(n==0){ return 0; }else if(n==1||n==2){ return 1; }else if(n>2){ int temp=fnType1(n-1)+fnType1(n-2); if(temp<0){ throw new Exception("Invalid value for int type, too larage"); }else{ return temp; } }else{ throw new Exception("IllegalArgument value for n,please enter n>=0 "); } } /** * 时间换空间 * @Title: fnType2 * @Description: TODO * @param @param n * @param @return * @return int (n<0 return -1,beyond max int size return -2) * @throws */ public int fnType2(int n){ int result=-1; int temp1=0; int temp2=1; for(int index=0;index<=n;index++){ if(index==0){ result=temp1; }else if(index==1){ result=temp2; }else{ result=temp1+temp2; if(result<0){ result=-2; break; } temp1=temp2; temp2=result; } } return result; } private static List<Integer> fnData=new ArrayList<Integer>(); private static final int maxSize=50000; /** * 空间换时间 * @Title: setFnData * @Description: TODO * @param * @return void * @throws */ private static void setFnData(){ int result=-1; int temp1=0; int temp2=1; for(int index=0;index<=maxSize;index++){ if(index==0){ result=temp1; }else if(index==1){ result=temp2; }else{ result=temp1+temp2; if(result<0){ result=-2; break; } temp1=temp2; temp2=result; } fnData.add(result); } } /** * * @Title: getFnData * @Description: TODO * @param @param n * @param @return * @return int (n beyond fnData.size() and n<0 return -1) * @throws */ public int getFnData(int n){ if(fnData.size()==0){ setFnData(); } if(fnData.size()>n&&n>=0){ return fnData.get(n); }else{ return -1; } } /** * * @Title: main * @Description: TODO * @param @param argv * @return void * @throws */ public static void main(String[] argv){ Init init=new Init(); int n=46; try { System.out.println("Type1="+init.fnType1(n)); } catch (Exception e) { // TODO Auto-generated catch block System.out.println(e.getMessage()); } System.out.println("Type2="+init.fnType2(n)); System.out.println("Type3="+init.getFnData(n)); } }
输出结果:
Type1=1836311903 Type2=1836311903 Type3=1836311903
对于算法设计,不要盲目遵循概念,概念是死的,人是活的(在这个需要crazy man的时代,有想法比循规蹈矩更有优势),只用结合具体的应用场景才能设计出优秀的算法和结构。
吐槽一下:个人认为优秀的数据结构设计可以简化算法设计的复杂度提高代码的可读性、程序的扩展性及执行效率;
再吐槽一下:做需求分析的时候应该遵循三点原则:1.从用户角度及其思维方式分析;2.用户说的不一定是他们真正想要的;3.用户说的不一定是对的。做程序开发遵循原则:积极提升自身品味,站在用户使用角度和使用场景分析功能;例如你做后台接口开发,你的用户就是接口调用者,你应该考虑接口功能是什么,使用者在什么情况下会调用,传入参数可能导致哪些异常,你的接口实现中可能出现哪些异常并对可能出现的异常进行捕获,清楚明了的输出,良好的函数自闭性;如果你是搞前台,那你应该在保证业务实现的基础上从用户使用习惯等方面把自己当做使用者来设计UI。很有意思对不,需求、开发多了自然就明白了O(∩_∩)O~。
更多java实现斐波那契数列的3种方法相关文章请关注PHP中文网!
上一篇: 顺时针方向打印矩阵解决思路