hdu 1568 求斐波數的前四位
阿新 • • 發佈:2018-12-24
2007年到來了。經過2006年一年的修煉,數學神童zouyu終於把0到100000000的Fibonacci數列
(f[0]=0,f[1]=1;f[i] = f[i-1]+f[i-2](i>=2))的值全部給背了下來。
接下來,CodeStar決定要考考他,於是每問他一個數字,他就要把答案說出來,不過有的數字太長了。所以規定超過4位的只要說出前4位就可以了,可是CodeStar自己又記不住。於是他決定編寫一個程式來測驗zouyu說的是否正確。 input: 輸入若干數字n(0 <= n <= 100000000),每個數字一行。讀到檔案尾。 output:輸出f[n]的前4個數字(若不足4個數字,就全部輸出)。
(f[0]=0,f[1]=1;f[i] = f[i-1]+f[i-2](i>=2))的值全部給背了下來。
接下來,CodeStar決定要考考他,於是每問他一個數字,他就要把答案說出來,不過有的數字太長了。所以規定超過4位的只要說出前4位就可以了,可是CodeStar自己又記不住。於是他決定編寫一個程式來測驗zouyu說的是否正確。 input: 輸入若干數字n(0 <= n <= 100000000),每個數字一行。讀到檔案尾。 output:輸出f[n]的前4個數字(若不足4個數字,就全部輸出)。
剛開始看到這麼大的資料,於智商有限的我,表示無能為力,於是YY數論的東西,最後以無結果告終- -!
後搜到AC的部落格中一篇此題的詳細剖析過程,頓時對核武的膜拜又升了一個檔次.......
要點:
1. log取對數的運用
2. Fib數的通項公式
先看對數的性質,loga(b^c)=c*loga(b),loga(b*c)=loga(b)+loga(c);
假設給出一個數10234432,那麼log10(10234432)=log10(1.0234432*10^7)=log10(1.0234432)+7;
log10(1.0234432)就是log10(10234432)的小數部分.
log10(1.0234432)=0.010063744
10^0.010063744=1.023443198
那麼要取幾位就很明顯了吧~
先取對數(對10取),然後得到結果的小數部分bit,pow(10.0,bit)以後如果答案還是<1000那麼就一直乘10。
注意偶先處理了0~20項是為了方便處理~
這題要利用到數列的公式:an=(1/√5) * [((1+√5)/2)^n-((1-√5)/2)^n](n=1,2,3.....)
取完對數
log10(an)=-0.5*log10(5.0)+((double)n)*log(f)/log(10.0)+log10(1-((1-√5)/(1+√5))^n)其中f=(sqrt(5.0)+1.0)/2.0;
log10(1-((1-√5)/(1+√5))^n)->0
所以可以寫成log10(an)=-0.5*log10(5.0)+((double)n)*log(f)/log(10.0);
最後取其小數部分。
#include<iostream> #include<cmath> #include<cstring> using namespace std; int main() { int f[22]; f[0] = 0; f[1] = 1; f[2] = 1; int i; for(i = 3 ; i <= 20 ; i ++) f[i] = f[i-1] + f[i-2]; int n; while(~scanf("%d",&n)) { if(n < 21) { printf("%d\n",f[n]); continue; } double e , k; k = -0.5*log10(5) + n*1.0*log10((1+sqrt(5))/2.0)+log10(1-pow((1-sqrt(5))/(1+sqrt(5)),n)); k = k - floor(k); e = pow(10.0,k); while(e < 1000) { e = e*10; } printf("%d\n",(int)e); } }