最著名的 NP-Hard 問題:精確解的極限在哪、啟發式又能多接近最優

給定 \(n\) 個城市與兩兩距離 \(d(i,j)\),求一條經過每城恰一次並回到起點的最短迴路。判定版是 NP-Complete; NP-Hard(距離數值再小也難——對比背包的弱 NP-Hard)。一般距離下連常數倍近似都是 NP-Hard;度量 TSP(滿足三角不等式)才有 1.5 倍近似(Christofides)。

外送員今天有 30 個地址要跑。路線的好壞直接是油錢和時間:\(30!\approx 2.7\times10^{32}\) 種順序,比全宇宙的星星還多。但外送平台每天在幾秒內排出幾千條「夠好」的路線——本篇就是要講清楚精確夠好這兩條路各自怎麼走、走到哪會撞牆。

精確解:Held–Karp 位元 DP

狀態設計(上一篇的直接升級)

Hamiltonian 判存在用布林,這裡問最短,把布林換成距離:

\[dp[S][v] = \text{從城 } 0 \text{ 出發、恰好經過集合 } S \text{、目前在 } v \text{ 的最短長度}\]

初始\(dp[\{0\}][0]=0\)轉移\[dp[S\cup\{u\}][u] = \min\bigl(dp[S\cup\{u\}][u],\ dp[S][v] + d(v,u)\bigr)\] 答案\(\min_{v\neq 0}\ dp[\text{全集}][v] + d(v,0)\)。時間 \(O(2^n n^2)\)、空間 \(O(2^n n)\)

規模牆

\(n\) 轉移數 \(2^n n^2\) DP 表記憶體(double)
15 \(7.4\times10^6\) 4 MB
20 \(4.2\times10^8\) 168 MB
25 \(2.1\times10^{10}\) 6.7 GB
30 \(9.7\times10^{11}\) 258 GB

\(n\approx 22\) 是家用電腦的實際極限。但相比 \(n!\) 枚舉(\(n=20\)\(2.4\times10^{18}\))已是天壤之別,而且 \(O(2^n\,\mathrm{poly})\) 至今仍是一般 TSP 的最佳精確複雜度——60 年沒人打破。

啟發式一:最近鄰(Nearest Neighbor)

從起點開始,每步走向最近的未訪城市,最後回起點。\(O(n^2)\)、三行寫完。品質:平均比最優差 10–25%,最壞可達 \(\Theta(\log n)\) 倍——它會把「順路該撿的城市」留到最後變成昂貴的長跳。

廉價補救 1:多起點——每個城市當一次起點跑 \(n\) 遍取最好,\(O(n^3)\) 常見改善 5% 左右(見執行結果 Example 3)。

啟發式二:2-opt 局部搜尋

核心動作:拆兩邊、反轉重接

\(d(a,c) + d(b,e) < d(a,b) + d(c,e)\),把 tour 中的 \((a,b)\)\((c,e)\) 拆掉、中段反轉——直觀上是解開一個交叉

反覆掃描所有 \((i,j)\) 對直到無法改善(局部最優)。每輪 \(O(n^2)\),通常幾輪收斂。NN 建構 + 2-opt 改善是「兩分鐘寫完、效果驚人」的組合拳:實測小型歐氏實例常直接命中最優(見執行結果)。

再往上是 3-opt、Lin–Kernighan(LKH)——LKH 在十萬城市級實例上能到最優 1% 以內,是目前實務最強的 TSP 啟發式。思想同源:不斷做「拆 \(k\) 條邊重接」的局部改善。

手算範例:4 城市

距離矩陣(對稱):\(d_{01}=10, d_{02}=15, d_{03}=20, d_{12}=35, d_{13}=25, d_{23}=30\)

所有迴路只有 \((4-1)!/2 = 3\) 條本質不同: \[0\!\to\!1\!\to\!2\!\to\!3\!\to\!0:\ 10+35+30+20 = 95;\quad 0\!\to\!1\!\to\!3\!\to\!2\!\to\!0:\ 10+25+30+15 = \textbf{80};\] \[0\!\to\!2\!\to\!1\!\to\!3\!\to\!0:\ 15+35+25+20 = 95.\] 最優 80。NN 從 0 出發:最近是 1(10),1 的最近未訪是 3(25),再 2(30),回 0(15)——恰好也是 80。NN 不總是這麼幸運,Example 2 就沒中。

完整 C++ 程式

包含:Held–Karp(含 tour 還原)、NN、多起點 NN、2-opt、隨機歐氏實例產生器、與最優的 gap 統計、規模牆實測。編譯:

g++ -std=c++17 -O2 -Wall -Wextra -o tsp tsp.cpp

執行結果與解讀

=== Example 1: 4-city worked example ===
Held-Karp optimal = 80.00 (expect 80)  tour: 0-2-3-1-0
Nearest neighbor  = 80.00  tour: 0-1-3-2-0

=== Example 2: NN vs NN+2opt vs optimal (n=12, Euclidean) ===
optimal (Held-Karp): 301.09  (1.32 ms)
nearest neighbor   : 316.19  (+5.01% above optimal)
NN + 2-opt         : 301.09  (+0.00% above optimal, 0.00 ms)

=== Example 3: multi-start nearest neighbor ===
best of 12 starts: 311.10 (start=7, single-start was 316.19)

=== Example 4: the scaling wall ===
n=13 | HK 3.70 ms   (opt=338.65) | NN+2opt 0.01 ms (len=338.65, gap +0.00%)
n=16 | HK 38.10 ms  (opt=335.43) | NN+2opt 0.01 ms (len=335.43, gap +0.00%)
n=19 | HK 407.14 ms (opt=368.26) | NN+2opt 0.06 ms (len=368.26, gap +0.00%)
Held-Karp memory: 2^n * n doubles -> n=25 needs ~6.7 GB. Game over.

Example 1 中兩條 tour 方向相反(\(0\!-\!2\!-\!3\!-\!1\!-\!0\)\(0\!-\!1\!-\!3\!-\!2\!-\!0\))——同一條迴路的兩種走向,長度相同。Example 4 的時間欄是「指數牆」的現場直播:\(n\) 每 +3,Held–Karp 時間 \(\times\)10,而 NN+2-opt 幾乎不動還全中最優。別被小例騙:2-opt 在小型歐氏實例上常命中最優,\(n\) 上百後 gap 通常會落在 2–5%,這時就需要模擬退火、LKH 或本系列第 13 篇的遺傳演算法。

實務應用

  • 物流路線:UPS 的 ORION 系統每天為 5.5 萬條路線做 TSP 級優化,號稱一年省 3 億美元油錢。

  • 製造:PCB 鑽孔、雷射切割、3D 列印噴頭路徑——都是「訪問所有點的最短路」。

  • 晶片測試:探針測試順序優化。

  • 天文觀測排程:望遠鏡轉動角度最小化的觀測順序。

  • 里程碑:Concorde 求解器已精確解出 85,900 城的實例(花了 136 CPU 年)——「NP-Hard」與「特定實例解不出」是兩回事的最佳證明。

練習題

  1. 手算 5 城市完全圖(自訂距離)的 Held–Karp 表前兩層(\(|S|=2,3\) 的所有狀態)。

  2. 把 2-opt 改成「first improvement」(找到第一個改善就做)vs 現在的「best sweep」,比較收斂速度與最終品質。

  3. 實作 Or-opt(把連續 1–3 個城市搬到別處),疊在 2-opt 之後看還能擠出多少。

  4. \(n=100\) 隨機歐氏實例:NN、NN+2opt、多起點 NN+2opt 三者的 gap 統計(以多起點 2-opt 最好值當基準)。

  5. 挑戰:實作 MST 下界(最優 tour \(\geq\) MST 權重),觀察 2-opt 解與下界的夾擠區間。

小結

方法 時間 品質 適用時機
Held–Karp \(O(2^n n^2)\) 精確 \(n\leq 22\)
最近鄰 \(O(n^2)\) 平均 +10–25% 要秒出、當初始解
NN + 2-opt \(O(n^2)\)/輪 平均 +2–5% 通用首選
Christofides \(O(n^3)\) \(\leq 1.5\times\)(度量) 要理論保證
LKH 高(啟發式) +1% 以內 大型實例的王者

延伸閱讀:Applegate et al. The Traveling Salesman Problem: A Computational Study(Concorde 團隊);Lin & Kernighan (1973);Karlin, Klein & Oveis Gharan (2021) 打破 Christofides 1.5 界的隨機化演算法。