科學計算實務:軟體、驗證與可重現性

別重造輪子(不要自己重寫)

當你理解了一個演算法,自己動手寫它是很誘人的。你讀懂了高斯消去法怎麼運作,看出它不過是幾層巢狀迴圈,於是心想:我一個下午就能把它寫出來。「別重造輪子」就是那份來之不易的專業智慧:對於標準的數值任務,你幾乎總是「不」應該自己寫——你應該去拿一個成熟、被廣泛使用的函式庫來用。課本上的演算法與一個可信任的實作是兩回事,而兩者之間的鴻溝,正是無數職涯份量的微妙之處所棲居之地。

具體說,函式庫知道哪些你那個下午版本不知道的事?它會做樞紐選擇以避免除以極小的數(你的版本可能除以零,或災難性地放大捨入誤差)。它會做縮放以避開溢位與下溢。它會優雅地處理空矩陣、奇異矩陣、資料中的 NaN,而不是當掉或回傳垃圾。它在設計上就是向後穩定的,回傳某個鄰近問題的精確解。它很快,會派發給分塊的 BLAS。而且它多年來被數千名使用者用數百萬個測試案例跑過,所以你會花幾個月才找到的臭蟲,早已被找出來了。像 numpy.linalg.solve(A, b) 這樣的一行,正是那整段歷史的精煉成果。你的迴圈不是。

誠實的平衡:「別重造輪子」是預設,不是絕對的法則。重新實作一個演算法是「學會」它的絕佳方式,你應該在玩具問題上這麼做。也確實有需要自製程式碼的情況——一個專門到沒有任何函式庫涵蓋的問題,或一個尚不存在的研究方法。這條規則真正講的是「該把你稀少的可靠度預算花在哪裡」:把它花在你的問題真正需要的新穎科學上,而對一切標準的東西,站在經過測試的地基上。自己重寫的危險,不在於它會明顯地失敗;而在於它會在困難的情況下默默給出略微錯誤的答案,而你會信任它們。

一個天真的、學生自己寫的 A x = b 求解器,若做高斯消去卻「不」做樞紐選擇,對於完全可解的 2x2 方程組(第一列為 (1e-20, 1)、第二列為 (1, 1)),會除以一個接近零的樞紐而產生垃圾。LAPACK 的 dgesv 會自動做樞紐選擇並回傳正確答案。這兩段程式實作的是「同一個」演算法;只有一個是可信任的。

同一個課本演算法,但函式庫版本會做樞紐選擇、縮放,並在困難情況下存活下來。

請務必為了理解而重新實作一個演算法——那正是學習之道。只是不要把你的重新實作放進正式產品、取代一個經過測試的函式庫。風險是在邊界情況下無聲、微妙的錯誤,而非大聲的失敗。

又稱
use a librarydon't reinvent the wheelreuse battle-tested code使用成熟函式庫不要重造輪子