用Fibonacci來對比遞歸和非遞歸,並優化遞歸效率。


一直以來對於遞歸只是了解使用,最近在看javascript相關方面的書籍,看到用記憶功能優化遞歸,第一反應就是C#完全也可以實現,隨即便測試了一下遞歸的各種方式。

首先先來看一下javascript的記憶遞歸:

var fibonacci = function () {
                var memo = [0, 1];
                var fib = function (n) {
                    var result = memo[n];
                    if (typeof result !== 'number') {
                        result = fib(n - 1) + fib(n - 2);
                        memo[n] = result;
                    }
                    return result;
                };
                return fib;
            }();

我們在一個名為memo的數組里保存我們的儲存結果,儲存結果可以隱藏之閉包中,當函數被調用是,這個函數首先檢查結果是否已存在,如果已經存在,就立即返回這個結果。

正文:C#的遞歸和非遞歸


先來一個大家非常熟悉的普通遞歸:

static int Fibonacci(int n)
{
      return n < 2 ? n : Fibonacci(n - 1) + Fibonacci(n - 2);
}

這個遞歸大家一目了然我就不做過多的解釋了,我們來看一下測試結果

 

現在來看一個我在網上找到的一個非遞歸模式:

static int Fibonacci(int n)
{if (n <= 0)
      {
           return n;
      }

      int a = 1;
      int b = 1;

      for (int i = 3; i <= n; i++)
      {
           b = (a + b);
           a = b - a;
      }
      return b;
}

由於遞歸的時間復雜度和空間復雜度較大,而且函數調用本身也會增加性能的開銷,所以每多遞歸一次,所損耗的時間成本近乎成倍增加,而非遞歸模式內部做了循環調用,減少了遞歸調用的次數,測試結果

 

再來一個我根據javascript的記憶功能做的一個C#遞歸:

//聲明一個記錄遞歸值的集合
static List<int> array = new List<int>() { 0, 1 };
static int Fibonacci(int n)
{
      int result = 0;
      if (array.Count <= n)
      {
           result = Fibonacci(n - 1) + Fibonacci(n - 2);
           array.Add(result);
      }
      else
      {
           result = array[n];
      }
      return result;
}

直接來看測試結果:

我們將每次遞歸的結果存到集合中,下次遞歸前先查詢集合中是否當前遞歸值的結果,如果有直接返回,減少了遞歸調用的次數,時間的損耗也大大減少了。

還有一種遞歸方式把遞歸的的損耗也優化的非常不錯,我們來看一下尾遞歸:

static int Fibonacci(int n, int ret1, int ret2)
{
       if (n == 0)
       return ret1;
       return Fibonacci(n - 1, ret2, ret1 + ret2);
 }

第一個參數是遞歸次數,第二和第三個參數的遞歸開始的基礎值,例如當前函數的調用方式:Fibonacci(10,0,1).我們來看一下測試結果:

通過對比我們發現尾遞歸運行的時間更短,效率更高,在實際應用中我們可以根據自己的需求去選擇不同的方式實現,但是在此建議如果可以用非遞歸實現盡量不要用遞歸,遞歸可能會造成堆棧溢出,但是也並非否定遞歸,最終的決定權還是在開發者手中。

本文以上代碼部分來自網絡,如有侵權,請聯系本人。

本文略顯簡陋,只用是用於博主記憶,若有錯誤,請指出,會做即使修改,謝謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM