1. 程式人生 > 實用技巧 >2.9 Fibonacci數列

2.9 Fibonacci數列

2.9 Fibonacci數列

序言

先看一下優美的Fibonacci螺旋曲線

畫圖程式碼

import turtle 
a, b = 0, 1
while a < 1000:
    turtle.circle(a, 90) 
    a, b = b, a+b

基礎問題

解法

  • 解法1 : 遞迴

  • 解法2 : 求解通項公式

    解法2 : 求解通項公式

    \[F(n) = F(n-1) + F(n-2) \]

    由此得到特徵方程:

    \[x^2 = x + 1 \]

    有根

    \[x_1 = \frac{1+\sqrt{5}}{2},x_2 = \frac{1-\sqrt{5}}{2} \]

    所以存在A,B使得

    \[F(n) = A * (\frac{1+\sqrt{5}}{2})^n + B*(\frac{1-\sqrt{5}}{2})^n \]

    代入\(F(0) = 0,f(1) = 1\)\(A=\frac{\sqrt{5}}{5},B=-\frac{\sqrt{5}}{5}\)

    \[F(n) = \frac{\sqrt{5}}{5} * (\frac{1+\ sqrt{5}}{2})^n - \frac{\sqrt{5}}{5} * (\frac{1-\sqrt{5}}{2})^n \]

  • 解法3 : 分治策略

  • 解法4 : 動態規劃

// 2.9 Fibonacci 數列
// import java.util.Math;
class Test{
	public static void main(String[] args) {
		/**
		## 基礎問題
		F(n) = 
			0 , if n==0
			1 , if n==1
			F(n-1) + F(n-1) , if n>1
		
		> 解法
			- 解法1 : 遞迴
			- 解法2 : 求解通項公式
			- 解法3 : 分治策略
			- 解法4 : 動態規劃
		## 拓展問題
		*/
		System.out.println(Fib1(10));
		System.out.println((int)Fib2(10));
		System.out.println(Fib3(10));
		System.out.println(Fib4(10));
		System.out.println(Fib5(10));
	}
	/**
	解法1 : 遞迴
	*/
	public static int Fib1(int n){
		if(n<=0) return 0;
		else if(n==1) return 1;
		else return Fib1(n-1) + Fib1(n-2);
	}
	/**
	解法2 : 求解通項公式
	$$F(n) = F(n-1) + F(n-2)$$
	由此得到特徵方程:
	$$x^2 = x + 1$$
	有根
	$$x_1 = \frac{1+\sqrt{5}}{2},x_2 = \frac{1-\sqrt{5}}{2}$$
	所以存在A,B使得
	$$F(n) = A * (\frac{1+\frac{5}}{2})^n + B*(\frac{1-\sqrt{5}}{2})^n$$
	代入$F(0) = 0,f(1) = 1$得$A=\frac{\sqrt{5}}{5},B=-\frac{\sqrt{5}}{5}$
	$$F(n) = \frac{\sqrt{5}}{5} * (\frac{1+\frac{5}}{2})^n - \frac{\sqrt{5}}{5} * (\frac{1-\sqrt{5}}{2})^n$$
	通過公式,我們可以在O(1)時間複雜度求得,但是引入了無理數,所以不能保證結果的精度
	*/
	public static double Fib2(int n){
		return Math.sqrt(5.0)/5.0*Math.pow((1.0+Math.sqrt(5.0))/2.0,n) - Math.sqrt(5.0)/5.0*Math.pow((1.0-Math.sqrt(5.0))/2.0,n);

	}
	/**
	解法3 : 分治策略 矩陣
	*/
	public static int Fib3(int N){
        if (N <= 1) 
      		return N;
        int[][] A = new int[][]{{1, 1}, {1, 0}};
        matrixPower(A, N-1);

        return A[0][0];

	}

    public static void matrixPower(int[][] A, int N) {
        if (N <= 1) {
          return;
        }
        matrixPower(A, N/2);
        multiply(A, A);

        int[][] B = new int[][]{{1, 1}, {1, 0}};
        if (N%2 != 0) {
            multiply(A, B);
        }
    }

    public static void multiply(int[][] A, int[][] B) {
        int x = A[0][0] * B[0][0] + A[0][1] * B[1][0];
        int y = A[0][0] * B[0][1] + A[0][1] * B[1][1];
        int z = A[1][0] * B[0][0] + A[1][1] * B[1][0];
        int w = A[1][0] * B[0][1] + A[1][1] * B[1][1];

        A[0][0] = x;
        A[0][1] = y;
        A[1][0] = z;
        A[1][1] = w;
    }
	/**
	解法4 : 動態規劃
	*/
	public static int Fib4(int n){
		int[] dp = new int[n+1];
		dp[0] = 0;
		dp[1] = 1;
		for(int i =2;i<= n;i++) dp[i] = dp[i-1] + dp[i-2];
		return dp[n];
	}
		/**
	解法4.2 : 動態規劃優化
	*/
	public static int Fib5(int n){
		int a = 0;
		int b = 1;
		int c = 0;
		for(int i = 2;i<=n;i++){
			c=a+b;
			a = b;
			b=c;
		}
		return c;
	}
}

拓展問題

假設\(A(0) = 1,A(1) = 2,A(2) = 2\),對於\(n>2\),都有\(A(k) = A(k-1) + A(k-2) + A(k-3)\)
1,對於任何一個n,如何計算出\(A(n)\)
2,對於n非常大的狀況,比如\(n=2^{60}\),如何計算\(A(n) mod N (M < 100000)\)呢?

答案

利用上述優化的dp