在以往解決需要遞歸求解的問題上一直使用傳統遞歸,而不久前老師講解了尾遞歸感覺需要記錄一下(好記性不如爛筆頭)
尾遞歸特點:在普通尾調用上,多出了2個特征。
1.在尾部調用的是函數自身(Self-called)
2.可通過優化,使得計算僅占常量棧空間(Stack Space)
舉個例子:
斐波那契數列(Fibonacci sequence),又稱黃金分割數列、因數學家列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖為例子而引入,故又稱為“兔子數列”,指的是這樣一個數列:1、1、2、3、5、8、13、21、34、……在數學上,斐波納契數列以如下被以遞推的方法定義:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)在現代物理、准晶體結構、化學等領域,斐波納契數列都有直接的應用。
下面代碼僅求斐波那契數列的第n項為多少,而不求前n項和。
1 public class Fibonacci { 2 3 public static void main(String[] args) { 4 int n = 50; 5 long begin1 = System.currentTimeMillis(); 6 System.out.printf("%d\n", fibonacci(n)); 7 long end1 = System.currentTimeMillis(); 8 System.err.println("花費時間:" + (end1 - begin1) + "毫秒"); 9 10 long begin2 = System.currentTimeMillis(); 11 System.out.printf("%d\n", advanced(n, 0L, 1L)); 12 long end2 = System.currentTimeMillis(); 13 System.err.println("花費時間:" + (end2 - begin2) + "毫秒"); 14 } 15 16 static long fibonacci(int n) { 17 if (n < 0) 18 return -1; 19 if (n <= 1) 20 return n; 21 return fibonacci(n - 1) + fibonacci(n - 2); 22 } 23 24 static long advanced(int n, long ret1, long ret2) { 25 if (n < 0) 26 return -1; 27 if (n == 0) 28 return ret1; 29 if (n == 1) 30 return ret2; 31 return advanced(n - 1, ret2, ret1 + ret2); 32 } 33 34 }
結果顯示:
計算fibonacci數列第50項。
一些初學的想法:傳統的遞歸相當於樹狀圖計算,而尾遞歸相當於循環計算
譬如計算數字階乘時:假設計算8!,傳統遞歸理解為8*7*6*5*4*3*2*Factorial(1);遞歸函數中留有出口,直到遞歸到出口參數為1時才返回值。
尾遞歸相當於循環計算,我看做為循環遞歸。計算8!就循環8次。函數形如:
static long advanced(int n, int r) { if (n < 0) { return -1; } else if (n == 0) { return 1 * r; } else { return advanced(n - 1, r * n); } }
其中第一個參數為循環遞歸次數,第二個參數為每一步循環計算出的數,作為新的參數繼續進行遞歸。(簡單來說就是算出階乘的每一步值作為新的參數)
函數返回自身函數,計算最終答案,進行下一函數計算時,不在依賴於上一函數,減少了棧空間的開辟。
ps:感覺類似於一串數的正反相乘過程。