Monday, January 5, 2009

小數點後多少位才夠

前些日子,家裡的長官因為指導學生科展的緣故,需要計算根號二、根號三的數值到小數點後三千位,在網路上找了兩天,始終找不到合適的資料。幾經思索,她發現找到資料的最佳方法,就是把任務交代下去,於是這個任務就落到我頭上,並且責令鄙人於兩日內限時完成任務。

每個對程式設計有興趣有野心的初學者,都曾經想過寫作自己的程式庫,任意位數四則運算無疑是一個不錯的練手標的,甫接到任務指令時,原也興致勃勃,覺得正是好好活動筋骨的機會,大不了自己寫個模組,再到網上找個 Newton's Method(牛頓法) 的算法就可交差。

仔細研究維基百科裡關於 Arbitrary-precision arithmetic 的說明,發現事情可沒有那麼容易,僅僅是要把任意位數乘法做出來,連規劃帶測試除錯,恐怕就不是一個晚上可以完工的。依照維基百科的說明,高明的算法,甚至要用到快速傅立葉轉換咧,這豈是一個週末項目(weekend project)的格局。所以,上策還是做個善用工具的人吧!

就在同一份資料裡,介紹了一些好心人撰寫並公開使用權的程式庫,我一一查訪,發現一個 Python 的模組mpmath , 完全符合需求。安裝 mpmath 非常容易,下載解壓縮之後,只要在檔案所在的目錄下,執行 sudo python setup.py install 就成了;需要引用這個模組的函數前,記得先載入 (from mpmath import *)這個模組,就算完成一切準備動作。

真正的重頭戲也簡單得不得了,閱讀文件之後發現,只要在進行運算前宣告本次運算的位數精度,比如說要計算三千位(3000 digits)的數字,就執行 mp.dps = 3000。mpmath 只問你要計算多少位元(digit),不管你要把小數點放在哪裡, dps 宣告數值是多少,mpmath 就給你那麼長的數字。

求根號二的過程簡單到出乎意料,先載入 mpmath (from mpmath import *), 宣告需要的精度(mp.dps=100),宣告一個多精度的浮點數(a=mpf(2.0)),然後直接用 ** 運算子求出答案(開根號是二分之一次方),用經過最佳化的 sqrt() 函數也可以達成相同目標。運算過程看下面截圖就一目了然:


我另外寫了一個 Newton's Method 的副程式,驗算的結果發現,不管求到小數點後多少位(我最多測到5000位),a**0.5 的答案和牛頓法求出來的結果是完全一樣的,而且用函式庫內建的函式速度更快。結論很簡單:在這個年代,要做個善用工具的人,才能快速完成任務。

1 comment:

如果我的心是一朵蓮花

~ 林徽因 · 馬雁散文集 · 蓮燈 ~ 馬雁 在她的散文《高貴一種,有詩為證》裡,提到「十多年前,還不知道林女士的八卦及成就前,在期刊上讀到別人引用的《蓮燈》」 覺得非常喜歡,比之卞之琳、徐志摩,別說是毫不遜色,簡直是勝出一籌。前面的韻腳和平仄的處理顯然高於戴...