1. 程式人生 > >關於調和級數問題(14屆藍橋杯b組第二題)

關於調和級數問題(14屆藍橋杯b組第二題)

今天,不對,準確說是昨天(不知不覺就凌晨了),又做了14屆藍橋杯b組第二題,猛一看,感覺藍橋杯b組的題不是我的菜,太簡單是一個求調和級數的問題,題如下:

1/1 + 1/2 + 1/3 + 1/4 + … 在數學上稱為調和級數。
它是發散的,也就是說,只要加上足夠多的項,就可以得到任意大的數字。
但是,它發散的很慢:
前1項和達到 1.0
前4項和才超過 2.0
前83項的和才超過 5.0
那麼,請你計算一下,要加多少項,才能使得和達到或超過 15.0 呢?
請填寫這個整數。
注意:只需要填寫一個整數,不要填寫任何多餘的內容。比如說明文字。

正確程式碼如下:

#include <stdio.h>
const int MAXN = 1e7; int main(int argc, const char * argv[]) { double sum = 0; int i = 1; for (; i < MAXN; i++) { sum = sum + 1.0 / i; if (sum >= 15.0) { printf("%d\n", i); break; } } return 0; }

一看題感覺沒啥含金量,於是隨手就做了起來,可是做後一直無法正常執行,搞的我好為難,於是自習審查,發現我一開始將sum和i都定義成了int型,說到int型函式我就要說道說道了,曾經對int型只知道這是個整形函式,但是並沒有從本質上研究過他的作用,於是我就去找了度娘。終於,我對int函式有了更深點的理解,int型函式是向下取整函式,例如:int(3.8)=3,int(-3,8)=-4,主體強調的是向下取整。範圍[-2^31 , 2^31 - 1] 即 [-2147483648,2147483647]。然而這道題涉及到了小數,所以我就順勢將兩個都定義成了float型,但是依然無法順利編譯,細細品來(在我看來,程式碼是藝術,需要品味),發現我犯了一個不經常涉及的小問題,那就是在“1.0/i”我寫成了“1/i”,於是知錯就改的我及時更正了問題,這是問題又出現了,我再次編譯,答案是4(因為我一開始為了檢驗程式碼,所以條件語句中條件寫的是“sum>=2.0”),正確,於是我就又改成了“sum>=15.0”,奇蹟出現了,答案竟然是一串不明字元“1.67386e+06”,頓時我蒙圈了,心裡想著怎麼破怎麼破,此時,我開始理思路,前面的幾個都成功,為什麼到這裡卻不行了呢?會不會是答案範圍的問題?答案是i,那麼會不會是i的範圍的問題?i是浮點型……什麼,等等,我為啥要用浮點型呢?明明輸出的答案都是整形,我卻用的浮點型,先不管其是否可以編譯成功,就這個情況定義這個型別也不符合程式碼的規範性啊!於是抱著試一試的心態的我將i改為了int型,奇蹟再次出現,編譯成功了,結果是“1673859”,正確。雖說這個程式成功了,但是我依然心存疑慮為什麼資料一大,用float型就失敗了呢?是不是也是資料範圍的問題?於是,度娘,我又來了……

問過度娘後,我總算是明白了,浮點數使用 IEEE(電氣和電子工程師協會)格式。浮點型別的單精度值具有 4 個位元組,包括一個符號位、一個 8 位 excess-127 二進位制指數和一個 23 位尾數。尾數表示一個介於 1.0 和 2.0 之間的數。由於尾數的高順序位始終為 1,因此它不是以數字形式儲存的。此表示形式為 float 型別提供了一個大約在 3.4E–38 和 3.4E+38 之間的範圍。

範圍的問題導致了輸出結果的紊亂,看來這道題的含金量也是有的,但主要不是考察我們的演算法,而是考察我們的細心程度與對整型和浮點型的認知深度。

真是辛苦出題人了,向出題人致敬!

2017.3.26修正

這是我初學程式設計時的一些小插曲,現在看看那時寫的部落格,感覺自己十分幼稚,但是這大概是初學者畢竟的一個過程吧~~~評論區有朋友說我的程式碼錯了,當時我也只是寫出來這個程式碼並沒有提交,那時候對 OJ 還沒有什麼概念,以為自己出結果就是正確的,現在看來真是滑稽的狠啊,這裡的確不能用 float,資料小的時候還沒問題,但是當資料大的時候,導致小數特別的小,已經無法用 float 完美的解決它了,只能動用更高精度的雙精度,畢竟 float 只能表示六位左右有效數字,而 double 則能表示15位左右,如果沒有記錯的話,感謝評論區的小夥伴給我指出我曾經的問題,以前的博文我也不想做什麼修改,畢竟這是我曾經的故事啊,所以只修改了文中程式碼,為了防止有的朋友只看看程式碼就走了,這樣我就可能提供了一個誤人子弟的程式碼啦,我擔不起責任,所以只修正一下程式碼,如果願意看我白話的人的話,那麼他一定會看到我這裡的重新解釋的。