`
singleant
  • 浏览: 378151 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

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

阅读更多

 

现象

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

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

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

以下面一个简单的例子来说:

(注:为了描述简单,所以这里只用一个简单的例子。这个例子可以用更简单的数学公式来实现,所以请大家不要过分在意。 重点是想说明递归可能带来的一些问题)

输入参数:N

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

两种实现代码如下:

 

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参数)和因为创建栈帧和切换的性能开销,最终大大的影响效率!

 

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

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

 

  • 大小: 79.2 KB
分享到:
评论
32 楼 clarkhill 2014-11-13  
汉诺塔其实有数学公式能够解决。
31 楼 kingsword 2011-09-04  
稍微给楼主纠个字面上的小不妥“java将为每个线程维护一个堆栈,堆栈里将为每个方法保存一个栈帧,”那个“堆栈”说成是Java栈好些,说成是堆栈,容易给初学者造成迷惑。
30 楼 hdenghui 2011-04-08  
学习,学习…
29 楼 tangqs 2011-04-08  
不过对于递归很深的程序由于很容易引发 栈溢出异常,所以在这样的情况下,用非递归算法更合适一些。
28 楼 tangqs 2011-04-08  
在Java中,JVM对递归调用有优化的,所以在很多情况之下递归调用的效率会比非递归高,lz的递归程序的例子并没有普遍意义,因为这个非递归程序没有用到栈结构,如果你真的要做例子,你可以在试试快速排序的递归和非递归算法。
27 楼 seanla 2011-04-08  
kimmking 写道
cttnbcj 写道
除非疯子才一个一个算logx相加 .......
向楼上所说递归是用来简单化来表达数据处理的思想

要是不用递归试试计算 f(n)=f(n-1)+f(n-2)+f(n-3)+f(n-4)+f(n-5)
不管是用非递归或推数学公式(上面公式已经要计算一元五次方程的解了),会非常复杂

五次方程没有根式解,
手工计算5个根有点累。
计算机计算起来比较容易。

可以考虑用演化计算,选择一个比较好的算子,很快就能算出最优解
26 楼 optimism_best 2011-04-07  
好贴,递归的用法还不是很清楚,在N很大的情况下递归的性能确实不乐观
25 楼 wolf521hf 2011-04-07  
学习了    
24 楼 sdh5724 2011-04-07  
用递归的时候, 得清楚自己是在干么, 否则就不要用。 就跟用正则一样得道理。  很多东西没有完美。用的时候都得想清楚。
23 楼 beykery 2011-04-07  
也要看什么情况啊楼主,有些算法递归实现确实很清晰,还有些算法把递归写成非递归形式还非常困难。比如alpha beta剪支我就不知道怎么写成非递归。
22 楼 gundumw100 2011-04-07  
个人感觉递归用于for循环次数不定的情况.
for
  for
    for
      ......
21 楼 realvalkyrie 2011-04-07  
用迭代来表示,即满足递归的明了,性能也相对来说可以接受:
结果以参数的形式传递,这样可以省去递归由于要保存栈信息而带来的开销。
20 楼 jackra 2011-04-07  
递归本身针对可扩展性多状况结构的数据有很强大处理能力.
如果要做一些可扩展的组件,递归是不可避免的.
不会递归可耻;滥用递归也可耻;不会正确的使用递归同样可耻。
19 楼 kamhung 2011-04-07  
不是递归本身慢, 是java调用方法慢, 例子1跟例子2的区别在于例子2少调用了方法, 这两个例子都运行几次就会发现两个耗时会越来越接近, 因为java对频繁调用的方法进行优化~
18 楼 Crusader 2011-04-07  
递归是用来简化求解难度的
但如果迭代次数过多的话,会影响性能
17 楼 math_xl 2011-04-07  
递归 代码比较整洁

空间消耗代价
16 楼 schweigen 2011-04-07  
在Sun JDK 下跑,第一次确实差距挺大,再运行第二次的结果(也许可能要多运行几次):

recursive result:7.212475098340103E7
recursive time used:312802755
non-recursive result:7.212475098340103E7
non-recursive time used:312842609

JDK 对尾递归还是会自己做优化的
15 楼 freish 2011-04-07  
每每看到递归都后怕,StackOverflowError是个心理阴影
14 楼 singleant 2011-04-07  
cttnbcj 写道
除非疯子才一个一个算logx相加 .......
向楼上所说递归是用来简单化来表达数据处理的思想

要是不用递归试试计算 f(n)=f(n-1)+f(n-2)+f(n-3)+f(n-4)+f(n-5)
不管是用非递归或推数学公式(上面公式已经要计算一元五次方程的解了),会非常复杂


你根本就不理解本帖子要说明什么! 只是想说明递归的开销!
例子不是最重要的,"除非疯子才一个一个算logx相加 ....... "这个大家都懂得,不要过分去在意这个。
13 楼 kimmking 2011-04-07  
cttnbcj 写道
kimmking 写道
cttnbcj 写道
除非疯子才一个一个算logx相加 .......
向楼上所说递归是用来简单化来表达数据处理的思想

要是不用递归试试计算 f(n)=f(n-1)+f(n-2)+f(n-3)+f(n-4)+f(n-5)
不管是用非递归或推数学公式(上面公式已经要计算一元五次方程的解了),会非常复杂

五次方程没有根式解
手工计算5个根有点累。
计算机计算起来比较容易。

你确定无解?只是无代数根式解
五次方程符合Galois群理论,就可以有解。。。。(x-a)(x-b)(x-c)(x-d)(x-e)=0 a,b,c,d,e都不相等,这个五次方,你说有解吗?

相关推荐

    java递归算法

    java递归算法,java递归算法,java递归算法

    用java实现的经典递归算法

    本文主要探讨如何使用Java实现经典递归算法,旨在帮助初学者理解递归的工作原理及其应用。递归算法设计的关键在于将复杂问题分解为相似的子问题,直到子问题简单到可以直接解决。这通常涉及到两个要素:递归出口和...

    快速选择非递归与递归算法实现

    空间复杂度方面,非递归实现主要取决于分区操作和栈的使用,而递归实现则依赖于递归深度,一般情况下都是O(log n)。 在实际编程中,可以根据具体需求选择非递归或递归实现。非递归版本更适合内存有限或者递归深度...

    Java递归算法(PPT+PDF+Word)

    总的来说,这个资料包提供了全面的递归算法学习资源,包括理论讲解、实例解析和可视化展示,对于想要深入理解Java递归算法的开发者来说,是一份宝贵的参考资料。通过学习这些内容,你将能够更好地掌握递归的概念,...

    递归算法Java实现

    根据题目提供的代码片段,我们可以看到这是一个使用Java实现的递归算法示例。该程序的主要目的是计算某个函数`fun`的值,其中函数`fun`接受一个整数参数`x`。 ```java public class test1 { Scanner scan = new ...

    Java递归算法构造JSON树形结构

    Java 递归算法构造 JSON 树形结构是指通过 Java 语言使用递归算法将数据库中的菜单表构建成树形的 JSON 格式发送给第三方。这种方法可以将复杂的树形结构数据转换成易于理解和处理的 JSON 格式。 在 Java 中,使用...

    java 用递归实现字符串反转

    ### Java使用递归实现字符串反转 在Java编程语言中,递归是一种常用的方法来解决许多问题,特别是那些可以通过分解成更小子问题来解决的问题。本文将详细介绍如何使用递归来实现字符串的反转。 #### 一、递归基础...

    java编写的递归算法的经典事例

    本文将详细介绍一个用Java编写的递归算法实例,该实例用于实现字符数组的所有可能全排列。通过这个例子,我们可以深入理解递归的基本概念、工作原理以及如何在实际编程中应用递归来解决问题。 #### 代码解析 #####...

    Java实现用递归算法和非递归算法求解斐波那契数列问题.docx

    ### Java实现用递归算法和非递归算法求解斐波那契数列问题 #### 知识点解析 在给定的文档标题与描述中,“Java实现用递归算法和非递归算法求解斐波那契数列问题”明确指出了本文将围绕Java编程语言、递归算法与非...

    Java递归算法jid

    此外,递归算法的实现方式可以非常复杂,需要确保递归过程中有一定的终止条件,否则程序将进入无限循环。 下面是一个简单的 Java 递归函数示例,用于计算斐波那契数列的第 n 个数: public class Fibonacci { ...

    15个典型的递归算法的JAVA实现

    15个典型的递归算法的JAVA实现,求N的阶乘、欧几里德算法(求最大公约数)、斐波那契数列、汉诺塔问题、树的三种递归遍历方式、快速排序、折半查找、图的遍历、归并排序、八皇后问题(回溯、递归)、棋盘覆盖(分治,...

    JAVA递归实现全排列

    JAVA递归实现全排列算法,含实现源代码,如a、b、c、d的全排列为: abcd abdc acbd acdb adcb adbc bacd badc bcad bcda bdca bdac cbad cbda cabd cadb cdab cdba dbca dbac dcba dcab dacb dabc

    Java二分查找递归算法

    Java二分查找递归算法

    java数据结构递归算法

    在这个"java数据结构递归算法"主题中,我们将深入探讨递归的基本概念、如何在Java中使用递归,以及一个著名的递归应用案例——八皇后问题。 递归是函数或方法调用自身的过程。它基于一个问题的规模缩小至基本情况,...

    用递归算法实现整数逆序

    ### 用递归算法实现整数逆序 #### 背景与意义 在计算机科学领域,递归算法是一种常用且强大的技术手段。通过将问题分解为更小规模的子问题来解决,递归能够有效地简化复杂度较高的计算任务。本篇文章主要探讨如何...

    Java实现汉诺塔递归算法详解

    要解决这个问题,我们可以使用递归策略。以下是一个简单的Java程序来实现汉诺塔问题的解决方案: ```java public class Hanoi { public static void moveTower(int n, char from, char inter, char to) { if (n =...

    java递归算法浅谈

    Java 递归算法是 Java 编程中的一种常见算法,通过自调用函数实现复杂问题的解决。下面是 Java 递归算法的相关知识点。 一、递归函数的定义 递归函数是指在函数体内直接或间接地调用自己,即函数的嵌套是函数本身...

    java 快速排序 折半查找的界面实现 (递归与分治法)

    这通常需要使用Java的Swing或JavaFX库来构建。用户可以输入一组数字,点击“快速排序”按钮进行排序,然后输入要查找的值,点击“折半查找”按钮进行搜索,结果会显示在界面上。 总的来说,快速排序和折半查找是...

Global site tag (gtag.js) - Google Analytics