Java與算法之(3) - 斐波那契數列

斐波那契數列問題:若是一對兔子每個月能生1對小兔子,而每對小兔在它出生後的第三個月裏,又能開始生1對小兔子,假定在不發生死亡的狀況下,由一對初生的兔子開始,1年後能繁殖出多少對兔子?

首先手工計算來總結規律,以下表

注意總數這一列

1+1=2

1+2=3

2+3=5

3+5=8

5+8=13

能夠得出規律,第n個斐波那契數=第n-1個斐波那契數+第n-2個斐波那契數

爲了計算n,必須計算n-1和n-2;爲了計算n-1,必須計算n-2和n-3;直到n-x的值爲1爲止,這顯示是遞歸大顯身手的地方。來看代碼java

public class Fibonacci { public static long calc(long n) { if(n < 0) { return 0; } if(n == 0 || n == 1) { return n; } else { return calc(n - 1) + calc(n - 2); } } }

這真是極短的,測試代碼函數

public static void main(String[] args) { long n = 50; long begin = System.nanoTime(); long f = Fibonacci.calc(n); long end = System.nanoTime(); System.out.println("第" + n + "個斐波那契數是" + f + ", 耗時" + TimeUnit.NANOSECONDS.toMillis(end - begin) + "毫秒"); }

運行輸出

第50個斐波那契數是12586269025, 耗時66024毫秒

注意看消耗的時間,在個人電腦上耗時66秒,真是個至關耗時的操做。既然整個過程都是在不斷重複相同的計算規則,那咱們能夠採用分而治之的思想來優化代碼。

測試

import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask; import java.util.concurrent.TimeUnit; public class Fibonacci extends RecursiveTask<Long> { long n; public Fibonacci(long n) { this.n = n; } public Long compute() { if(n <= 10) {  //小於10再也不分解
            return Fibonacci.calc(n); } Fibonacci f1 = new Fibonacci(n - 1);  //分解出計算n-1斐波那契數的子任務
        f1.fork();  //由ForkJoinPool分配線程執行子任務
        Fibonacci f2 = new Fibonacci(n - 2);  //分解出計算n-2斐波那契數的子任務
        return f2.compute() + f1.join(); } public static long calc(long n) { if(n < 0) { return 0; } if(n == 0 || n == 1) { return n; } else { return calc(n - 1) + calc(n - 2); } } public static void main(String[] args) { long n = 50; long begin = System.nanoTime(); Fibonacci fibonacci = new Fibonacci(n); ForkJoinPool pool = new ForkJoinPool(); long f = pool.invoke(fibonacci); long end = System.nanoTime(); System.out.println("第" + n + "個斐波那契數是" + f + ", 耗時" + TimeUnit.NANOSECONDS.toMillis(end - begin) + "毫秒"); } }

運行輸出

第50個斐波那契數是12586269025, 耗時20461毫秒

雖然時間縮短了2/3,可是仍然不理想。回頭從新看計算方法,用遞歸方式雖然代碼簡短,可是存在很嚴重的重複計算,下面用非遞歸的方式改寫,過程當中每一個數只計算一次。

  優化

public static long calcWithoutRecursion(long n) { if(n < 0) return 0; if(n == 0 || n == 1) { return n; } long fib = 0; long fibOne = 1; long fibTwo = 1; for(long i = 2; i < n; i++) { fib = fibOne + fibTwo; fibTwo = fibOne; fibOne = fib; } return fib; }

 



測試

第50個斐波那契數是12586269025, 耗時0毫秒

斐波那契數的另外一個經典題目是青蛙跳臺階問題:

一隻青蛙一次能夠條一級或兩級臺階,求該青蛙跳上n級的臺階共有多少種跳法。
假設計算第n級臺階跳法的函數是f(n),當n>2時,第一步選擇跳一級有X種跳法,第一步選擇跳兩級有Y種跳法,f(n)=X+Y。如何計算X呢,站在青蛙的位置考慮,面對的是一個全新的n-1級臺階,有f(n-1)種跳法,那麼Y就是n-2級臺階的跳法,那麼f(n)=f(n-1)+f(n-2),即斐波那契數列公式。this


---------------------
原文:https://blog.csdn.net/autfish/article/details/52370830

spa

相關文章
相關標籤/搜索