JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

兩種大O:精度的階與成本的階

同一個符號 O(...) 在數值計算裡身兼兩職:它既衡量誤差縮得多快,也衡量工作量漲得多快。學會讀懂兩者,並問出唯一真正重要的問題——每秒能買到多少精度。

一個符號,兩個故事

到這裡你已經知道:每個數值答案都是一個近似值,而我們用絕對誤差相對誤差來衡量它錯得多離譜。本篇要加上整個領域最好用的一個速記符號:大O符號。麻煩之處——也是初學者混淆的根源——在於同一個字母 O 被用來描述兩件完全不同的事。一個 O 描述你更努力時誤差如何變化;另一個 O 描述那份努力到底有多大。

把寫下「O(h^2)」或「O(n^3)」想成一次口語化的聳肩:丟掉常數,只留下趨勢。我們不在乎誤差究竟是 3.7 h^2 還是 0.02 h^2;我們在乎的是把 h 減半,誤差大約會變成四分之一。我們也不在乎一次求解花的是 2 n^3 還是 0.5 n^3 次運算;我們在乎的是把 n 加倍,速度大約會慢八倍。大O保留斜率,扔掉雜訊。

嚴格地說,當 h 趨近 0 時 f(h) = O(h^p) 的意思是:存在某常數 C,使得對所有夠小的 h 都有 |f(h)| <= C h^p。而當 n 增大時 g(n) = O(n^3) 的意思是:對夠大的 n 都有 g(n) <= C n^3。定義相同,極限相反:精度看著 h 縮向 0,成本看著 n 漲向無窮。整篇讀下來請記住這個方向感——它就是那只指南針,告訴你眼前的 O 在講的是哪一個故事。

大O故事一:精度的階,O(h^p)

許多方法都有一個叫步長 h 的旋鈕——網格的間距、模擬的時間步、差商所用的間隔。精度的階 p 告訴你誤差中的指數:誤差大約等於 C h^p。p 越大,縮小 h 時誤差消失得越快。一階方法 O(h),h 減半時誤差也減半;二階方法 O(h^2),誤差變四分之一;四階方法 O(h^4),誤差除以十六。實務上這個差別大得驚人。

那個 h^p 從哪來?幾乎總是來自泰勒展開。看最簡單的導數估計,有限差分公式 (f(x+h) - f(x))/h。泰勒展開說 f(x+h) = f(x) + h f'(x) + (h^2/2) f''(x) + ...,於是這個商等於 f'(x) + (h/2) f''(x) + ...,留下 O(h) 的誤差:一階。你砍掉的那些剩餘項,正是截斷誤差。換成中央差分 (f(x+h) - f(x-h))/(2h),奇次項相消,把它抬升到 O(h^2)。這場相消免費替你多買了一階。

大O故事二:成本,O(n^3)

第二個 O 衡量的是一個演算法隨問題規模 n 增大所做的工作——它的計算複雜度。對一個有 n 個未知數的稠密線性系統 A x = b,用高斯消去法(會產生一個 LU 分解)求解,大約花 (2/3) n^3 次算術運算,所以我們說 O(n^3)。這裡誠實的單位是浮點運算次數——浮點加法與乘法的總數。把 n 加倍,工作量大約漲八倍;光是這一條,就決定了一個模型是隔夜跑完還是永遠跑不完。

n        O(n)        O(n log n)   O(n^2)       O(n^3)
100      1e2         ~7e2         1e4          1e6
1,000    1e3         ~1e4         1e6          1e9
10,000   1e4         ~1.3e5       1e8          1e12
當 n 每漲十倍時,各個成本等級爆炸的速度。O(n log n) 與 O(n^3) 之間的鴻溝,正是快速傅立葉轉換與稀疏求解器值得那份巧思的原因。

好的數值方法的天才之處就藏在這裡。把兩個 n×n 矩陣相乘是 O(n^3);樸素的離散傅立葉轉換是 O(n^2),但快速傅立葉轉換把同一批求和重新編排成 O(N log N),把一小時變成一眨眼。又或者回想分解一次、求解多次:LU 分解花 O(n^3),但之後每換一個右側向量 b 求解只要 O(n^2)。若你必須對許多 b 求解 A x = b,那個三次方只付一次,二次方付很多次——絕不從頭重新消去。

讀出斜率:一個數值實驗

你很少需要相信課本對 p 的宣稱——你可以親自量它。用步長 h 與 h/2 各跑一次,比較誤差。若誤差是 C h^p,那麼把 h 減半,誤差應該除以 2^p。於是觀測到的比值 error(h)/error(h/2) 就洩露了 p:大約 2 是一階,大約 4 是二階,大約 16 是四階。這個小檢查是行內的日常工具。

  1. 選一個你已知真實答案的問題,這樣才能算出真正的誤差(例如估計 f'(x),而你知道精確導數)。
  2. 在步長 h 下跑該方法,記下誤差 e1;再在 h/2 下跑一次,記下 e2。
  3. 算出比值 e1/e2。取 p = log2(e1/e2):比值接近 4 就得到 p 接近 2,確認這是二階方法。
  4. 沿著一串遞減的 h 重複,看 p 是否穩定下來。若 p 突然下滑,你就撞上了捨入誤差的地板——別再縮小 h 了。

這個實驗直接接上下一篇關於收斂階的內容:像牛頓法這樣的迭代法有它自己的階 p(牛頓法在單根附近是二次收斂,p = 2),但那裡的 p 描述的是從一步 x_n 到下一步 x_{n+1} 的誤差如何縮小,而不是網格間距 h 如何影響單一答案。同樣是大O的想法,只是套用在一個數列上而非步長上。

唯一真正重要的問題:每秒換得的精度

現在把兩個 O 放進同一個房間,因為真實的抉擇正是兩者之間的權衡。高階方法(p 大)能用粗得多的網格達到目標精度——更少的點、更少的未知數、更低的成本。快速方法(複雜度小)則讓你在同樣的時間預算裡負擔得起更細的網格。實務工作者的問題從來不是單問「哪個更準」或「哪個更便宜」,而是合在一起問:哪個方法能在我擁有的時間與記憶體之內,交付我所需要的精度?

一個具體的張力:四階方法每個網格點的浮點運算可能比二階方法多,卻仍可能大獲全勝,因為它要達到同樣的誤差所需的點數少得多。但高階並非免費午餐——它通常要求問題具備額外的光滑性,而且在尖點、震波或含噪資料附近可能更脆弱,那裡反而是穩健的低階方法默默取勝。大O告訴你漸近趨勢;它對常數 C 保持沉默,也對「常數主導的小問題」保持沉默。

也要把你的兩個誤差來源分清楚。精度的大O只掌管離散化或截斷誤差——用有限步長作近似所付的代價。它對條件數隻字不提:一個高階得漂亮、便宜得完美的演算法,若底層問題本身病態,照樣產出垃圾,因為精度 = 條件數 × 穩定性,而不只靠演算法的巧思。大O替你編列努力的預算;它修不好一個本就病態的問題。