斐波那契數列及其對數時間算法
前些天做IEEE校內算法賽的時候,遇到了一道關於斐波那契數列的題,要求是對數時間;今天在牛課網上刷leetcode,看到爬樓梯問題,於是在網上搜索了一下,自己參考並總結了下斐波那契數列及其算法。主要參考了知乎這個問題下的回答最高贊回答。
斐波那契數列大家應該都很熟悉:0 1 1 2 3 5 8… ,遞推公式如下:
斐波那契數列的同向可以使用特徵根法或者矩陣的乘積來解決:
下面介紹利用矩陣快速冪的對數時間的斐波那契數列算法:
首先從遞推公式我們很容易得到:
不妨設 ,那麼我們的目標就是求矩陣A的n次冪。
在介紹矩陣的快速冪算法之前,我們先介紹一下數的快速冪算法,數的快速冪算法一般有遞歸和位運算兩種:
//遞歸版本
int my_power(int x, int n) {
//n >= 0
if(n == 0) return 1;
return n % 2 ? x * my_power(x, n / 2) : my_power(x, n / 2);
}
//位運算版本
int my_power(int x, int n) {
//n >= 0
int result = 1;
while(n) {
if(n & 1) result *= x;
x *= x;
n >>= 1;
}
return result;
}
例如求 , ,分二進制位上爲1和爲0兩種情況討論。
所以可以用同樣的方法求矩陣的冪,我們聲明瞭一個二階矩陣類,只給了矩陣的四個元素、默認構造函數和重載operator*
:
//Definition for 2-dimension matrix
struct Matrix {
/*[m00, m01]
[m10, m11]*/
int m00, m01, m10, m11;
Matrix(int a = 0, int b = 0, int c = 0, int d = 0) :
m00(a), m01(b), m10(c), m11(d) { }
Matrix& operator*(const Matrix& rhs) {
//若要對大數取餘,可以對下面的a,b,c,d取餘運算。
int a = this->m00 * rhs.m00 + this->m01 * rhs.m10; //% mod
int b = this->m00 * rhs.m01 + this->m01 * rhs.m11; //% mod
int c = this->m10 * rhs.m00 + this->m11 * rhs.m10; //% mod
int d = this->m10 * rhs.m01 + this->m11 * rhs.m11; //% mod
this->m00 = a; this->m01 = b;
this->m10 = c; this->m11 = d;
return *this;
}
};
/*對數算法,利用矩陣的快速冪*/
Matrix fast_multi(int n) {
Matrix m(1, 1, 1, 0), result(1, 0, 0, 1);
while(n) {
if(n & 1) result = result * m;
m = m * m;
n >>= 1;
}
return result;
}
因此斐波那契數列的第n項的算法爲:
//這裏沒有考慮int溢出
int Fibonacci(int n) {
Matrix m = fast_multi(n);
return m.m10;
}
若斐波那契數列的初值爲f0和f1:
//這裏沒有考慮int溢出
int Fibonacci(int n, int f0, int f1) {
Matrix m = fast_multi(n);
return (m.m10 * f1 + m.m11 * f0);
}
最開始說到IEEE校內算法賽的那道題是要求斐波那契數列的前n項和:
這個結論用數學歸納法是非常容易證明的。