漸進分析——大O、成長率與成本模型

隱藏常數的陷阱

大O記號以刻意丟掉常數倍率,換來它的與機器無關性,而這個習慣本身,在你忘了自己做過這筆交易時就成了陷阱。陷阱在於把漸進標籤當成完整的判決:斷定一個 O(n log n) 演算法在每種情況下「必定」勝過 O(n^2) 的,而現實中是被藏起來的常數與實際輸入規模決定了實務贏家。

具體地說,O(f) 表示「對大的 n 至多 c 乘 f」,而那個常數 c 可以是任何值——2 或 2000。所以一個 O(n log n) 但常數為 1000 的演算法,可能做 1000 乘 n log n 次運算,而一個 O(n^2) 但常數為 2 的演算法只做 2 乘 n^2 次;對交叉點(1000 n log n 等於 2 n^2 之處)以內的輸入,那個「較差」的平方演算法是真的更快。這不是矛盾——大O從未承諾相反的事。真實例子俯拾即是:簡單的插入排序(O(n^2))對小陣列勝過合併排序(O(n log n)),這正是為什麼實務上正式使用的排序常式在門檻以下會切換到插入排序;而矩陣乘法的「銀河級」演算法漸進上優於史特拉森,常數卻巨大到從不被使用。

紀律在於記住記號藏起了什麼。用大O來預測擴展、並在 n 大或將會大時於演算法間做選擇;但對小或固定的 n、或已知常數差距懸殊時,要量測而非假設。正確的心智模型是:漸進告訴你最終那場比賽的斜率,而非起跑線上誰領先。忽略常數對理論是優點,對工程是危險,而謹慎的實踐者同時握住這兩個真理。

函式庫的排序常式(如 introsort 或 Timsort)整體是 O(n log n),卻刻意對小於約 16 到 32 個元素的子陣列退回插入排序這個 O(n^2) 的方法——因為插入排序微小的常數使它在那個規模上快於合併或快速排序。漸進較差的演算法在小 n 時是實務贏家。

大O藏起常數;在交叉點以下,「較差」的演算法可能取勝。

較佳的大O僅是對大 n 的承諾。對小或固定的輸入,是常數倍率與交叉點在決定——所以要量測,別假設漸進較佳者必勝。

又称
constant factors matterthe big-O blind spot常數倍率盲點