程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 如果要用Java實現算法,一定慎用遞歸

如果要用Java實現算法,一定慎用遞歸

編輯:關於JAVA

現象 :

遞歸是我們很經典的一種算法實現,可以很好的描述一個算法的原理!對於算法的描述、表現和代碼結構理解上,遞歸都是不錯的選擇!

但是本文想說的是Java實現一個遞歸算法的時候盡量不要用遞歸實現,而是轉換成的非遞歸實現。

最近在實現一個比較復雜算法的時候,嘗試了一下,非遞歸實現相比遞歸實現速度上能提升1/3。

以下面一個簡單的例子來說:(注:為了描述簡單,所以這裡只用一個簡單的例子)

輸入參數:N

輸出結果: log1+log2+log3+....+logN

兩種實現代碼如下:

Java代碼

  1. package test;
  2. public class RecursiveTest {
  3. /**
  4. * 遞歸實現
  5. *
  6. * @param n
  7. * @return
  8. */
  9. public static double recursive(long n) {
  10. if (n == 1) {
  11. return Math.log(1);
  12. } else {
  13. return Math.log(n) + recursive(n - 1);
  14. }
  15. }
  16. /**
  17. * 非遞歸實現
  18. *
  19. * @param n
  20. * @return
  21. */
  22. public static double directly(long n) {
  23. double result = 0;
  24. for (int i = 1; i <= n; i++) {
  25. result += Math.log(i);
  26. }
  27. return result;
  28. }
  29. public static void main(String[] args) {
  30. int i = 5000000;
  31. long test = System.nanoTime();
  32. long start1 = System.nanoTime();
  33. double r1 = recursive(i);
  34. long end1 = System.nanoTime();
  35. long start2 = System.nanoTime();
  36. double r2 = directly(i);
  37. long end2 = System.nanoTime();
  38. System.out.println("recursive result:" + r1);
  39. System.out.println("recursive time used:" + (end1 - start1));
  40. System.out.println("non-recursive result:" + r2);
  41. System.out.println("non-recursive time used:" + (end2 - start2));
  42. }
  43. }

得到運行結果如下:

  1. recursive result:7.212475098340103E7
  2. recursive time used:539457109
  3. non-recursive result:7.212475098340103E7
  4. non-recursive time used:282479757

可以看出遞歸的運行時間是非遞歸運行時間將近2倍。

(注:以上代碼還是在-Xss200m的參數下運行的,如果棧空間不足,直接不能運行)

原因簡單分析:

上圖是java線程棧的結構。Java將為每個線程維護一個堆棧,堆棧裡將為每個方法保存一個棧幀,棧幀代表了一個方法的運行狀態。 也就是我們常說的方法棧。最後一個為當前運行的棧幀。

那麼每一次方法調用會涉及:

1.為新調用方法的生成一個棧幀

2.保存當前方法的棧幀狀態

3.棧幀上下文切換,切換到最新的方法棧幀。

遞歸實現將導致在棧內存的消耗(往往需要調整Xss參數)和因為創建棧幀和切換的性能開銷,最終大大的影響效率!

所以,如果你想提升你的算法效率,不要使用遞歸實現是一個基礎原則!

另外,遞歸是我們用來理解算法的一個方法,當用代碼來實現的時候基本都可以轉換成非遞歸的代碼實現!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved