在JAVA中求階乘首先遇到的問題就是結果溢出,無論是使用int仍是long,double都沒法表示1000!這麼大的天文數字,這裏暫且用BigInteger解決這個問題!java
下面是使用遞歸和尾遞歸分別計算1000的階乘:函數
1 import java.math.BigInteger; 2 3 public class Main { 4 5 public static void main(String[] args) { 6 long t = System.currentTimeMillis(); 7 System.out.println(factorial(new BigInteger("1000"))); 8 System.out.println(System.currentTimeMillis()- t); 9 t = System.currentTimeMillis(); 10 System.out.println(factorial2(new BigInteger("1000"),BigInteger.ONE)); 11 System.out.println(System.currentTimeMillis()- t); 12 } 13 14 15 /** 16 * 使用線性遞歸計算階乘 17 * @param n 18 * @return 19 */ 20 public static BigInteger factorial(BigInteger n ){ 21 if (n.compareTo(BigInteger.ZERO) < 0) return BigInteger.ZERO; 22 23 if (n.equals(BigInteger.ONE) || n.equals(BigInteger.ZERO)) { 24 return new BigInteger("1"); 25 } 26 return n.multiply(factorial(n.subtract(BigInteger.ONE))); 27 } 28 29 30 /** 31 * 使用尾遞歸計算階乘 32 * 若是一個函數中全部遞歸形式的調用都出如今函數的末尾,咱們稱這個遞歸函數是尾遞歸的。 33 * 當遞歸調用是整個函數體中最後執行的語句且它的返回值不屬於表達式的一部分時,這個遞歸調用就是尾遞歸。 34 * 尾遞歸函數的特色是在迴歸過程當中不用作任何操做,這個特性很重要,由於大多數現代的編譯器會利用這種特色自動生成優化的代碼。 35 * 尾遞歸是極其重要的,不用尾遞歸,函數的堆棧耗用難以估量,須要保存不少中間函數的堆棧。 36 * 經過參數傳遞結果,達到不壓棧的目的 37 * @param n 38 * @param result 39 * @return 40 */ 41 public static BigInteger factorial2(BigInteger n,BigInteger result){ 42 if (n.compareTo(BigInteger.ZERO) < 0) return BigInteger.ZERO; 43 44 if (n.equals(BigInteger.ONE) || n.equals(BigInteger.ZERO)) { 45 return result; 46 } 47 48 return factorial2(n.subtract(BigInteger.ONE),n.multiply(result)); 49 } 50 51 }
輸出:性能
402387260077093773543702433923003985719374864210714632543799910...(太長了,省略)000 38
402387260077093773543702433923003985719374864210714632543799910...(省略)000
11
Process finished with exit code 0
從上面的代碼和運行結果能夠看出,尾遞歸使得節省了中間函數堆棧的使用,使得性能大大提升(38-11=27毫秒)!優化