把本級串起來
在前四篇裡,我們建立了兩把完全各自獨立的尺。一把屬於問題:條件性問的是,當輸入被輕推一下,真正的答案會跑多遠,而條件數 kappa 就替這份敏感度標上數字——良態或病態是「問題」的性質,不是你程式碼的性質。另一把屬於演算法:穩定性問的是,你那一串特定的浮點步驟,有多忠實地貼著精確計算走,而後向穩定就是黃金標準——它表示你算出的答案,正好是某個被輕微擾動過的輸入的精確答案。
我們之所以一直把這兩個觀念分開,是因為它們最後會扣在一起,合成一句話。條件性衡量的是問題會把任何擾動放大多少。後向穩定的演算法則保證,它自己引入的擾動是極小的。把這兩件事串聯起來,你就得到本級一路爬向的那條規則——那條能預測你實際留得住幾位數的、短短的不等式。
主規則
就是這句話,請把它帶出本級:一個計算結果的相對前向誤差,至多等於條件數乘上相對後向誤差。前向誤差是你真正在乎的——答案錯了多少。後向誤差是好演算法能掌控的——要把輸入挪多遠,才能讓你的答案剛好正確。條件數則是兩者之間的匯率。這就是把「準確度 = 條件 × 穩定性」原理壓成一行。
forward error <= condition number x backward error
(answer wrong) kappa (the PROBLEM) (algorithm slip)
relative ~ machine epsilon
forward error <= kappa x (if BACKWARD STABLE)
~ 1e-16 (double)
example: kappa ~ 1e8 , backward error ~ 1e-16
=> forward error <= 1e8 x 1e-16 = 1e-8
=> keep ~8 of your ~16 digits ; the other ~8 are gone現在來看代入一個後向穩定演算法的魔力。它的後向誤差被壓在約機器 epsilon——在雙精度裡大約是 1e-16。於是不等式塌縮成:相對前向誤差至多等於 kappa 乘上 1e-16。演算法已經盡了它能做的一切;剩下的純粹是條件性。這正是你先前見過的經驗誤差界,也是為什麼一個接近 1e8 的條件數,會在你開始解讀結果之前,就悄悄吃掉約 16 個有效位數中的 8 個。
四種情況,兩個旋鈕
因為準確度是兩個因子的乘積,你能落腳的角落恰好有四個,而且認出自己在哪個角落,就完成了一半的除錯。把條件性和穩定性想成兩個各自獨立的旋鈕;只有當兩個旋鈕都轉對方向時,答案才會好。
- 良態問題 + 穩定演算法:最幸福的情況。kappa 小、後向誤差又接近 epsilon,所以前向誤差也接近 epsilon。你付了多少位數,就拿到多少位數。
- 良態問題 + 不穩定演算法:是你自己的錯,而且可以修。問題本身沒問題,但某個粗心的步驟(想想災難性抵消)注入了很大的後向誤差。改寫公式或換掉演算法,準確度就回來了。
- 病態問題 + 穩定演算法:誠實的損失。程式碼無罪、後向誤差接近 epsilon,但 kappa 巨大——所以前向誤差還是很大。沒有任何軟體小技巧能救你;唯有改變問題(重新表述、正則化、加精度)才有用。
- 病態問題 + 不穩定演算法:兩種壞處兼得,也最容易誤診——光看那個錯誤答案,你分不出是問題的錯還是程式碼的錯,除非你把兩個旋鈕分開來看。
為什麼完美的演算法仍會失敗
情況 3 值得我們停下來坐一會兒,因為它推翻了一個讓人安心的直覺。你也許會以為,只要程式碼沒有錯、數值上又謹慎——完全後向穩定——那答案就一定準確。主規則說:不。穩定性只界住後向誤差;前向誤差是那個界乘上 kappa。當 kappa 大得驚人時,就連無瑕的演算法,交回來的前幾位數也是垃圾——而且它真的是某個問題的正確答案,只是那個問題和你問的那個,差了一個捨入誤差。
一個具體畫面:解 A x = b,而 A 嚴重病態,比方說一個 Vandermonde 或類 Hilbert 矩陣,帶著很大的矩陣條件數。跑一個教科書級的穩定解法器,殘差 ||A x_computed - b|| 算出來極小——演算法是清白的——但 x_computed 可能從第一位數就和真正的 x 不同。從 b 到 x 的映射會把誤差拉伸 kappa(A) 倍,而沒有任何解法器的選擇能把它拉回去。順帶一提,這也部分解釋了為什麼你很少真的去算 A 的反矩陣來解 A x = b:明確地對一個病態矩陣求逆,是在放大傷害,而不是迴避它。
與這條規則共處
因為你能掌控的永遠只有兩個因子中的一個,你的策略也沿著同一條縫一分為二。你把演算法變好,靠的是選一個後向穩定的方法,並避開那些會摧毀數值穩定性的操作——但這頂多把後向誤差壓向 epsilon,永遠贏不過條件數。要再進一步,你就得改變問題:重新表述它、正則化它、把變數重新縮放,或退回更高的精度,讓 epsilon 本身變小,買回一些丟掉的位數。
同時,請把整門學問那個誠實的框架放在眼前。每個數值答案都是近似值;浮點數運算不是精確的實數運算——0.1 沒有精確的二進位形式,加法甚至不滿足結合律。「準確度 = 條件 × 穩定性」這條規則,是一面透鏡,告訴你對任何一次給定的計算,那份無可避免的近似有多少能存活到最後。它不保證你會得到好答案。它保證你會得到一份對「誤差從哪來」的誠實帳目——而正是這份帳目,讓你能採取行動,而不是瞎猜。