《編程之美》讀書筆記08:2.9 Fibonacci序列
計算Fibonacci序列最直接的方法就是利用遞推公式 F(n+2)=F(n+1)+F(n)。而用通項公式來求解是錯誤的,用浮點數表示無理數本來就有誤差,經過n次方后,當n相當大時,誤差能足夠大到影響浮點數轉為整數時的精度,得到的結果根本不準。
用矩陣來計算,雖然時間復雜度降到O(log n),但要用到矩陣類,相當麻煩。觀察:
F(n+2)=F(n)+F(n-1)=2*F(n-1)+F(n-2)=3*F(n-2)+2*F(n-4)
用歸納法很容易證明 F(n) = F(k)*F(n+1-k) + F(k-1)*F(n-k),利用該遞推公式和原遞推公式,要計算F(n),只要計算F([n/2])和F([n/2]+1),時間復雜度為 O(lg n)。如:要計算F(58),由 58 -> 29,30 -> 14,15 -> 7,8 -> 3,4 -> 1,2 可知只要算5次。可以用一個棧保存要計算的數,實際上,將n的最高位1(假設在第k位)左邊的0去除掉后,第m次要計算的數是第k位到第k-m+1位這m個位組成的值t(m),則第m-1次要計算的數為t(m-1),且
t(m)=2*t(m-1)+(第k-m+1位是否為1)。
若第m-1次計算得到了f(k)和f(k+1),則第m次計算:
第k-m+1位
|
已計算
|
待計算
|
1
|
f(k)
f(k+1)
|
f(2*k+1),f(2*k+2)
|
0
|
f(2*k),f(2*k+1)
|
具體公式見下面代碼。
下面是計算F(n)最后四位數(某道ACM題)的代碼。
/* Fibonacci數列第N個數的最后4位數
注意,當 N>93 時 第N個數的值超過64位無符號整數可表示的范圍。
F(n+2)=F(n)+F(n-1) F(0)=0 F(1)=1 F(2)=1 ==>
F(n)=F(k)*F(n+1-k) + F(k-1)*F(n-k) ==>
F(2*n)=F(n+1)*F(n)+F(n)*F(n-1)=(F(n+1)+F(n-1))*F(n)=(F(n+1)*2-F(n))*F(n)
F(2*n+1)=F(n+1)*F(n+1)+F(n)*F(n)
F(2*n+2)=F(n+2)*F(n+1)+F(n+1)*F(n)=(F(n+2)+F(n))*F(n+1)=(F(n+1)+F(n)*2)*F(n+1)
*/
unsigned fib_last4( unsigned num)
{
if ( num == 0 ) return 0;
const unsigned M=10000;
unsigned ret=1,next=1,ret_=ret;
unsigned flag=1, tt=num;
while ( tt >>= 1) flag <<= 1;
while ( flag >>= 1 ){
if ( num & flag ){
ret_ = ret * ret + next * next;
next = (ret + ret + next) * next;
} else {
//多加一個M,避免 2*next-ret是負數,造成結果不對
ret_ = (next + next + M - ret) * ret;
next = ret * ret + next * next;
}
ret = ret_ % M;
next = next % M;
}
return ret;
}