如果要用Java实现算法,一定慎用递归

发表于:2011-5-16 09:57

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:singleant    来源:51Testing软件测试网采编

#
java

  现象 :

  递归是我们很经典的一种算法实现,可以很好的描述一个算法的原理!对于算法的描述、表现和代码结构理解上,递归都是不错的选择!

  但是本文想说的是java实现一个递归算法的时候尽量不要用递归实现,而是转换成的非递归实现。

  最近在实现一个比较复杂算法的时候,尝试了一下,非递归实现相比递归实现速度上能提升1/3。

  以下面一个简单的例子来说:(注:为了描述简单,所以这里只用一个简单的例子)

  输入参数:N

  输出结果:log1+log2+log3+....+logN

  两种实现代码如下:

  Java代码

  • package test;     
  •     
  • public class RecursiveTest {     
  •     /**   
  •      * 递归实现   
  •      *    
  •      * @param n   
  •      * @return   
  •      */    
  •     public static double recursive(long n) {     
  •         if (n == 1) {     
  •             return Math.log(1);     
  •         } else {     
  •             return Math.log(n) + recursive(n - 1);     
  •         }     
  •     }     
  •     
  •     /**   
  •      * 非递归实现   
  •      *    
  •      * @param n   
  •      * @return   
  •      */    
  •     public static double directly(long n) {     
  •         double result = 0;     
  •         for (int i = 1; i <= n; i++) {     
  •             result += Math.log(i);     
  •         }     
  •         return result;     
  •     }     
  •     
  •     public static void main(String[] args) {     
  •         int i = 5000000;     
  •         long test = System.nanoTime();     
  •         long start1 = System.nanoTime();     
  •         double r1 = recursive(i);     
  •         long end1 = System.nanoTime();     
  •         long start2 = System.nanoTime();     
  •         double r2 = directly(i);     
  •         long end2 = System.nanoTime();     
  •     
  •         System.out.println("recursive result:" + r1);     
  •         System.out.println("recursive time used:" + (end1 - start1));     
  •         System.out.println("non-recursive result:" + r2);     
  •         System.out.println("non-recursive time used:" + (end2 - start2));     
  •     }     
  • }
  •   得到运行结果如下:

  • recursive result:7.212475098340103E7  
  • recursive time used:539457109   
  • non-recursive result:7.212475098340103E7  
  • non-recursive time used:282479757
  •   可以看出递归的运行时间是非递归运行时间将近2倍。

      (注:以上代码还是在-Xss200m的参数下运行的,如果栈空间不足,直接不能运行)

      原因简单分析:

      上图是java线程栈的结构。java将为每个线程维护一个堆栈,堆栈里将为每个方法保存一个栈帧,栈帧代表了一个方法的运行状态。 也就是我们常说的方法栈。最后一个为当前运行的栈帧。

      那么每一次方法调用会涉及:

      1. 为新调用方法的生成一个栈帧

      2. 保存当前方法的栈帧状态

      3. 栈帧上下文切换,切换到最新的方法栈帧。

      递归实现将导致在栈内存的消耗(往往需要调整Xss参数)和因为创建栈帧和切换的性能开销,最终大大的影响效率!

      所以,如果你想提升你的算法效率,不要使用递归实现是一个基础原则!

      另外,递归是我们用来理解算法的一个方法,当用代码来实现的时候基本都可以转换成非递归的代码实现!

    《2023软件测试行业现状调查报告》独家发布~

    精彩评论

    • gcd0318
      2011-5-16 14:28:08

      实验用的例子太简单,导致lz几乎忽略了编码的难度,而且其实这个问题的递归解法本来也不是最优,迭代当然更有效,但是有些问题,递归的劣势并不明显。理论上,所有的递归都可以用循环+栈解决,但是考虑到编码难度,递归经常可以胜出

    关注51Testing

    联系我们

    快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

    法律顾问:上海兰迪律师事务所 项棋律师
    版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
    投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

    沪ICP备05003035号

    沪公网安备 31010102002173号