當前位置:
首頁 > 知識 > 計算一列數字的平均值

計算一列數字的平均值

考慮一下下面的的問題:

你有一列浮點類型的數字。這絕不是令人討厭的惡作劇----沒有無窮個數字或無限大的數字,僅僅只是正常的「簡單的」浮點型的數字。

現在:計算其平均值。你能做到嗎?

事實證明這是一個很困難的問題,想要得到該平

使用Hypothesis庫來考慮以下的測試案列:

計算一列數字的平均值

這並不是關於正確性的測試,只是測試平均值是否在列表的合理的限制範圍內:在不作為平均值的情況下,有許多函數可以滿足這個要求。最小值和最大值函數都滿足這個要求,中值函數也是如此。

然而,幾乎沒有人的平均值計算方法滿足這個要求。

為了理解其中的原因,寫下了我們自己的平均值計算方法:

計算一列數字的平均值

這看起來十分合理--它正是平均值的定義--但是,它是錯誤的:

計算一列數字的平均值

其問題在於有限的浮點數可能足夠大,以至於它們的和溢出到無窮大。然後當你用無窮大除以一個有限的數時,你仍然會得到無窮大,這就意味著超出了範圍。

所以,為了阻止有限的浮點數之和的溢出,我們嘗試通過列表的長度來限制我們的數字大小:

計算一列數字的平均值

計算一列數字的平均值

在這種情況下,你遇到的問題不是溢出,而是浮點數的精度不夠:浮點數只能精確到一個整數的2次冪,因此除以3會導致舍入錯誤。在這種情況下我們有一個問題,就是(x/3)* 3一般不等於x本身。

所以,現在我們理解了為什麼求平均值可能非常困難。讓我們看看現有的方法是如何滿足這個測試的。

首先,我們嘗試使用numpy庫:

計算一列數字的平均值

這遇到了我們在第一次實驗中遇到的問題:

計算一列數字的平均值

Python3.4還提供了新的統計模塊。糟糕的是,這個模塊也出現了問題(在Python3.5.2中得到了修復):

計算一列數字的平均值

在之前溢出到無窮大的情況下,這反而會產生一個錯誤。該錯誤產生的原因是統計模塊在內部將所有數字都轉化成Fraction類型,這是一種任意精度的有理數類型。由於一些細節,即在何時何地被轉化為浮點數,這就產生了一個不容易被轉化為浮點數的有理數。

編寫一個通過測試的方法相對容易,僅僅需要簡單的作弊,而不需要實際計算出其平均值:

計算一列數字的平均值

也就是說,將值限制在期望的範圍內。

然而,編寫一個真正地,正確的平均值計算方法(可以通過測試的)是相當困難的:

為了理解其困難程度,這裡有一篇30頁的關於計算兩個數的平均值的論文。

如果我是你,我就不會去看那篇論文。我已經閱讀過這篇論文了,但我並沒有記得很多細節。

這個測試是一個很好的實例:一旦你編寫的代碼沒有崩潰,測試工作正常進行,就可以開始在結果值上添加額外的約束。正如本例所示,即使你添加的約束非常寬鬆,它也常常會捕獲到一些有趣的bug。

它還證明了一個問題:浮點數運算非常困難,這使得它不太適合用Hypothesis庫進行驗證。

這並不是因為Hypothesis庫不擅長測試浮點代碼,而是因為它善於向人們展示編程的實際難度,而浮點編碼比人們所預想的要難得多。

因此,你或許並不會在意它將發現的一些bug:一般來說,大多數人對於浮點數錯誤的態度是,」那些數字好奇怪,我們並不真的在意它們。或許已經足夠好了「。如果你希望你的浮點代碼是正確的,那麼數值敏感度分析工作是必不可少的,但是很少有人能夠完成這種高要求的工作。

我過去經常用這個例子來向人們演示Hypothesis庫,但由於這些問題,我不會再這樣做了:告訴人們他們並不想要修復的bug,既不會修復bug,也不會得到朋友。

但是,值得知道的是,這是一個問題:編程是非常困難的,而忽略這些問題並不會使它變得容易。你可以忽略正確性問題,直到它們真正給你造成麻煩為止,但是當它們給你帶來麻煩時,最好不要感到驚訝。

而且,一些通用的技術也值得被牢記,因為這不僅僅是對浮點數有用:大多數的代碼可以從中受益,而且大多數時候它告訴你的bug不會那麼令人不快。


英文原文:https://hypothesis.works/articles/calculating-the-mean/ 譯者:Lyx

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python部落 的精彩文章:

Python小貼士集錦
讀懂Python的Mock對象庫(2)

TAG:Python部落 |