第一卷悄悄做出的假設
在整個第一卷中,每一步高斯消元、每一個逆、每一個點積都是在精確算術下計算的。兩數相加,得到精確的和。解一個方程組,得到精確的解。這個世界在電腦上並不存在。真實機器把數字儲存在浮點運算中——一張由可表示值構成的有限網格,而每次運算都會把結果捨入到這張網格上。
最關鍵的數字是單位捨入誤差 u,在 IEEE 雙精度中約為 1.1e-16。模型很簡單:當你計算 a op b 時,機器返回的是被一個不超過 u 的相對誤差擾動後的真實結果,即 fl(a op b) = (a op b)(1 + d),其中 |d| <= u。單次運算幾乎毫無損失。但十億次運算串聯起來,麻煩就可能累積。
# The classic shock, in any IEEE-double language: 0.1 + 0.2 == 0.3 -> False 0.1 + 0.2 -> 0.30000000000000004 # 0.1 is not representable exactly; it is stored as the # nearest grid point, and the tiny error survives the add. # Catastrophic cancellation: subtracting near-equal numbers a = 1.0000000001 b = 1.0000000000 a - b -> 1.00000008274e-10 (only ~1 good digit left) # The leading digits agreed and cancelled; what remains is # dominated by each operand's rounding error.
後向誤差:評判答案的正確方式
問「我算出的 x 離真實 x 有多近?」(前向誤差)往往是錯誤的問題,因為對於病態問題,即便完美的演算法也無法接近。更好的問題是後向誤差:我算出的 x 是否恰好是某個被輕微擾動的問題的精確解?若一個演算法總是求解一個鄰近的問題——僅被 O(u) 擾動——它就是後向穩定的。這是黃金標準,而且即便前向誤差很大時也可達到。
這種區分令人釋然。當真凶其實是矩陣中固有的 1e14 量級條件數時,你不再為算出離譜的 x 而責怪自己的程式碼。它還給你一個具體的檢驗:計算殘差 r = b - A x_computed。若 ||r|| 相對於 ||A|| ||x|| 極小,那麼你的求解器就是後向穩定的,僅此而已——哪怕 x 本身看起來與真相毫不相像。
廉價的保險:迭代精化
當後向穩定還不夠、你還想把前向誤差也壓下來時,迭代精化常常幾乎免費地實現這一點。其思路是:你已經分解了 A 來得到 x,於是複用這個分解去求解修正方程組的代價很低。用更高精度計算殘差,求出修正量,加回去,再重複。
- 用穩定的分解先解一次 A x = b,得到 x_0。
- 構造殘差 r = b - A x_0,最好用擴展精度,使它攜帶真實資訊。
- 複用同一分解求解 A d = r——這很便宜,無需重新消元。
- 更新 x_1 = x_0 + d,然後迴圈直到殘差不再縮小。