算法列表
本文從時間效率和佔用空間內存角度評估,找出最優算法。
- 經典遞歸算法Recursive algorithm(很慢)
- 動態存儲算法Dynamic programming(慢)
- 矩陣冪算法Matrix exponentiation(快)
- 倍數公式算法Fast doubling(很快)
- 倍數公式算法+快速乘法Fast doubling with Karatsuba(最快)
Fibonacci數列
1.數列介紹
斐波那契數列(Fibonacci sequence),又稱黃金分割數列、因數學家列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖爲例子而引入,故又稱爲“兔子數列”,指的是這樣一個數列:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, …
2.數列規律
當我們將這些數字畫成正方形時,會得到一個螺旋擴大的圖形,如下。
數列的規律是:F(n)= F(n-1) + F(n-2)
而且當n趨向於無窮大時,前一項與後一項的比值越來越逼近黃金分割0.618(或者說後一項與前一項的比值小數部分越來越逼近0.618)。
1÷1=1,1÷2=0.5,2÷3=0.666…,3÷5=0.6,5÷8=0.625…,
55÷89=0.617977…144÷233=0.618025…46368÷75025=0.6180339886……
越到後面,這些比值越接近黃金比。
3.通用公式:
或者(φ爲 1.618034…)
經典遞歸算法
1.遞歸公式
F(n)= F(n-1) + F(n-2)
2.代碼
private static long i = 0;
public static long F(int n) {
if (n == 0)
return 0;
if (n == 1)
return 1;
i++;//記錄計算次數
return F(n-1) + F(n-2);
}
public static void main(String[] args) {
for (int n = 0; n < 40; n++)
System.out.println(n + " " + F(n)+ " " + i + " "+ (long)Math.pow(1.618, n+1));
}
輸出:
0 0 0 1
1 1 0 2
2 1 1 4
3 2 3 6
4 3 7 11
5 5 14 17
…….
35 9227465 39088132 33360044
36 14930352 63245948 53976552
37 24157817 102334116 87334061
38 39088169 165580101 141306511
39 63245986 267914255 228633935
3.評估
時間效率–>運算次數O(φ^n) 。n越大,越接近此值,見上最後兩列。
空間內存–>佔用內存O(n)。
動態存儲算法
將計算過的F(n-1)和F(n-2)儲存起來,不用再次計算。
1.代碼
public static long F(int n)
{
long a = 0, b = 1, c, i;
if( n == 0)
return a;
for (i = 2; i <= n; i++){
c = a + b;
a = b;
b = c;
}
return b;
}
public static void main(String[] args)
{
for (int n = 0; n < 100; n++)
System.out.println(n + " " + F(n));
}
2.評估
時間效率–>運算次數O(n) 。
空間內存–>佔用內存O(1)。
矩陣冪算法
1.公式
2.證明
3.代碼
public static long fib(int n)
{
long[][] f = {{1,1},{1,0}};
if (n == 0)
return 0;
if( n == 1)
return 1;
return power(f, n-1);
}
public static long power(long[][] f, int n)
{
if( n == 0)
return 0;
if( n == 1)
return 1;
long[][] m = {{1,1},{1,0}};
power(f, n/2);
long x = multiply(f, f);
if (n % 2 != 0)
x = multiply(f, m);
return x;
}
public static long multiply(long[][] f, long[][] m)
{
long x = f[0][0]*m[0][0] + f[0][1]*m[1][0];
long y = f[0][0]*m[0][1] + f[0][1]*m[1][1];
long z = f[1][0]*m[0][0] + f[1][1]*m[1][0];
long w = f[1][0]*m[0][1] + f[1][1]*m[1][1];
f[0][0] = x;
f[0][1] = y;
f[1][0] = z;
f[1][1] = w;
return x;
}
public static void main(String[] args)
{
for (int n = 0; n < 100; n++)
System.out.println(n + " " + fib(n));
}
4.評估
時間效率–>運算次數O(logn) 。
空間內存–>佔用內存O(1)。
倍數公式算法
1.公式
2.證明
在矩陣冪公式基礎上。
3.代碼
放到下一個算法裏。
倍數公式算法+快速乘法
1.公式
還是跟倍數公式算法一樣,只是裏面的乘法用快速乘法替代。
首先介紹快速乘法。
Karatsuba乘法是一種快速乘法算法。由Anatolii Alexeevitch Karatsuba於1960年提出,並於1962年發表。一般我們做高精度乘法,普通算法的複雜度爲O(n^2),而Karatsuba算法的複雜度爲O(3n^log3) ≈ O(3n^1.585)。
2.證明
Karatsuba算法主要應用於兩個大數的相乘,原理是將大數分成兩段後變成較小的數位,然後做3次乘法。推導過程如下:
計算兩個很大的數xy相乘,取一個正整數m。
3.代碼
//倍數公式算法
private static long Fibonacci(int n) {
long a = 0;
long b = 1;
for (int i = 31; i >= 0; i--) {//n最大爲2^31-1,所以只需要移位32次
long d = a * (b * 2 - a);
long e = a * a + b * b;
a = d;
b = e;
if (((n >> i) & 1) != 0) {
long c = a + b;
a = b;
b = c;
}
}
return a;
}
//倍數公式算法+快速算法
private static long FibonacciWithKaratsuba(int n) {
long a = 0;
long b = 1;
for (int i = 31; i >= 0; i--) {
long d = Karatsuba(a, b * 2 - a);
long e = Karatsuba(a, a) + Karatsuba(b, b);
a = d;
b = e;
if (((n >> i) & 1) != 0) {
long c = a + b;
a = b;
b = c;
}
}
return a;
}
//快速算法
public static long Karatsuba(long x, long y){
if((x < 10) || (y < 10)){
return x * y;
}
String s1 = String.valueOf(x);
String s2 = String.valueOf(y);
int maxLength = Math.max(s1.length(), s2.length());
int m = (int)Math.pow(10, maxLength/2);//取10 的(maxLength長度一半)次冪爲除數
long xHigh = x / m;
long xLow = x % m;
long yHigh = y / m;
long yLow = y % m;
long a = Karatsuba(xHigh, yHigh);
long b = Karatsuba((xLow + xHigh), (yLow + yHigh));
long c = Karatsuba(xLow, yLow);
return a * m * m + (b - a - c) * m + c;
}
public static void main(String[] args) {
for (int N = 0; N < 100; N++)
System.out.println(N + " " + Fibonacci(N) + " "+ FibonacciWithKaratsuba(N));
}
輸出結果一樣。
0 0 0
1 1 1
2 1 1
3 2 2
4 3 3
5 5 5
….
45 1134903170 1134903170
46 1836311903 1836311903
47 2971215073 2971215073
48 4807526976 4807526976
49 7778742049 7778742049
50 12586269025 12586269025
4.評估
時間效率–>運算次數O(logn) 。
空間內存–>佔用內存O(1)。
算法時間效率對比
運行環境:
Intel Core 2 Quad Q6600 (2.40 GHz)
單線程
Windows XP SP 3, Java 1.6.0_22.
單位ns。
參考資料:
https://www.nayuki.io/page/fast-fibonacci-algorithms