今天面試,被問到求fabonacci數列的第n個數這個問題,當時用O(n)複雜度的迭代方法做出來了,然後面試官繼續問了如何實現O(log(n))的時間複雜度的算法,最後還問了n大概爲多大時結果就會溢出(用unsigned int來保存結果)。
Fabonacci數列
Fabonacci數列是指數列中的任一項都等於前兩項之後,通項公式爲:
一般令
O(n)時間複雜度的方法
第一種方式就是用迭代的方式,每次只需要保存兩個連續的值,就可以生成數列中的下一個數了。
// O(n) iterative method
int64_t Fabonacci1( int n ) {
if ( n <= 1 ) return n;
int64_t prev = 0, cur = 1, next = 0;
for ( int i = 2; i <= n; ++i ) {
next = prev + cur;
prev = cur;
cur = next;
}
return cur;
}
O(logn)時間複雜的方法一
Fabonacci數列有一個變形,可以由此得到通項公式。然後利用通項公式加速計算。
有了通項公式,就可以直接求出F(n)了。明顯的對於G(n),可以求G(n/2),再由G(n/2)*G(n/2)來得到G(n),然後每次這樣折半,最後到G(1)或G(0)。不過需要注意的是要區分n爲奇數和偶數的情況,具體來說,當n爲奇數的時候,n/2+n/2 = n - 1,所以當n爲奇數的時候指數需要加1。這樣算法就是O(logn)的時間複雜度啦。
// O(logn) method1
void multiply( int64_t m[2][2], int64_t n[2][2] )
{
auto x = m[0][0] * n[0][0] + m[0][1] * n[1][0];
auto y = m[0][0] * n[0][1] + m[0][1] * n[1][1];
auto z = m[1][0] * n[0][0] + m[1][1] * n[1][0];
auto w = m[1][0] * n[0][1] + m[1][1] * n[1][1];
m[0][0] = x; m[0][1] = y;
m[1][0] = z; m[1][1] = w;
}
void power( int64_t num[2][2], int n ) {
if ( n <= 1 ) return;
int64_t m[2][2] = { { 1, 1 }, { 1, 0 } };
power( num, n / 2 );
multiply( num, num );
if ( n & 0x1 )
multiply( num, m );
}
/*
| F(n+1) F(n) | | 1 1 |n-1 | F(2) F(1) | | 1 1 |n
| F(n) F(n-1) | = | 1 0 | * | F(1) F(0) | = | 1 0 |
*/
int64_t Fabonacci2( int n ) {
int64_t m[2][2] = { { 1, 1 }, { 1, 0 } };
if ( n == 0 )
return 0;
power( m, n - 1 );
return m[0][0];
}
O(logn)時間複雜的方法二
上面利用通項公式求Fabonacci數列的方式還是比較麻煩的。下面給出一個更簡單一點的方式。
由上面可知
進一步可以得到:
繼續變形,得到:
所以總結就是:
當n爲偶數時,令k=n/2,
當n爲奇數時,令k=(n+1)/2,
// O(logn) method2
int64_t Fabonacci3( int n ) {
static vector<int64_t> f;
if ( f.size() <= n ) {
f.resize( 2 * n, 0);
}
if ( n == 0 ) return 0;
if ( n == 1 || n == 2 ) return ( f[n] = 1 );
if ( f[n] ) return f[n];
// if n is odd then n&1 is 1
if ( n & 1 ) {
int k = ( n + 1 ) >> 1;
f[n] = Fabonacci3( k ) * Fabonacci3( k )
+ Fabonacci3( k - 1 ) * Fabonacci3(k - 1);
}
else {
int k = n >> 1;
f[n] = ( 2 * Fabonacci3( k - 1 ) + Fabonacci3( k ) ) * Fabonacci3( k );
}
return f[n];
}
Fabonacci通用公式
其實fabonacci數列是有通用的公式的,可以直接求得結果。
其中
下面來證明這個表達式。原方程兩邊同時乘以
令
求得
所以
Fabonacci 數列上下界
得到公式了其實就可以比較容易得到上界限,
下界其實是
可以用歸納法證明。
參考
http://www.geeksforgeeks.org/program-for-nth-fibonacci-number/
http://math.stackexchange.com/questions/571433/exponential-lower-bound-for-fibonacci-numbers
http://math.stackexchange.com/questions/674533/prove-upper-bound-big-o-for-fibonaccis-sequence