跳轉到

1971 - Cook 的 NP-完全理論(計算複雜度的分水嶺)#

案件摘要#

1971 年,Stephen Cook(1939–) 在 STOC 會議發表 The Complexity of Theorem-Proving Procedures:

\[ \boxed{\text{發現了第一個「NP-完全」問題——SAT}} \]

Cook 定理:

\[ \boxed{\text{SAT(布林可滿足性)是 NP-完全的——所有 NP 問題都可以歸約到 SAT}} \]

P vs NP 的問題(千禧年七大問題之一,獎金 100 萬美元):

\[ \boxed{P \stackrel{?}{=} NP \quad\text{(「容易驗證」的問題是否也「容易求解」?)}} \]

1982 年,Cook 獲得圖靈獎—— 這是計算理論 50 年來最重要的未解問題。

前因 -- 為什麼會有這個案子#

  • 多項式 vs 指數(1950s–1960s):
  • 有些演算法快(多項式):排序 \(O(n\log n)\) 、最短路徑 \(O(V^2)\) ;
  • 有些演算法慢(指數):旅行推銷員 \(O(2^n)\) 、子集合和 \(O(2^n)\) 。
\[ \text{為什麼有些問題「天生」就慢?} \]
\[ \text{是「還沒找到快的演算法」,還是「原理上不存在」?} \]
  • Cobham 的類別(1964): Alan Cobham(1964)提出多項式時間是「高效計算」的數學定義:
\[ P = \bigcup_k \text{TIME}(n^k) \quad\text{(多項式時間可解)}. \]

這是「快 vs 慢」的分界線(雖然 \(n^{100}\) 也是多項式——理論的抽象)。 - Cook 的洞察(1971): Cook 在研究定理證明的複雜度時,發現:

\[ \text{任何 NP 問題的計算過程都可以被「編碼」成一個布林公式} \]
\[ \Rightarrow \text{解決 SAT 就解決了所有 NP 問題!} \]
\[ \boxed{\text{SAT 是 NP 的「最大公因數」(NP-完全)}} \]
  • Levin 的平行發現(1973): Leonid Levin(蘇聯)在 1973 年獨立發現了同一理論—— Cook–Levin 定理(冷戰時代的雙重發現,與 Lobachevsky–Bolyai 相呼應)。
  • Karp 的 21 個問題(1972): Richard Karp(1985 圖靈獎)把 Cook 的理論發揚光大—— 證明了 21 個經典問題都是 NP-完全的:
\[ \text{旅行推銷員、子集合和、分區、頂點覆蓋、漢米頓迴路、圖著色……} \]
\[ \boxed{\text{NP-完全的問題無所不在}} \]

線索與推理 -- 數學式、程式、理論#

第一條線索:P、NP、NP-完全的定義#

類別 定義 例子
P 多項式時間可解 排序、最短路徑、線性規劃
NP 多項式時間可驗證 SAT、TSP、圖著色
NP-完全 NP 中「最難」的 SAT、3-SAT、TSP(決策版)
PSPACE 多項式空間可解 棋類遊戲
\[ \boxed{P \subseteq NP \quad\text{(可解當然可驗證)}} \]

問題:反向是否成立?

\[ P = NP \ ? \]

若成立:密碼學崩潰(RSA 可破)、最佳化全解; 若不成立(多數人相信):有些問題原理上無法快速求解。

第二條線索:Cook 定理(SAT 是 NP-完全)#

定理(Cook–Levin, 1971/1973):

\[ \boxed{\text{SAT} \in \text{NP-完全}} \]

證明大綱(「編碼計算過程」): 1. 對任何 NP 問題 \(L\) 與輸入 \(x\) ,存在非決定性圖靈機 \(M\) 在 \(n^k\) 時間內驗證; 2. 把 \(M\) 在 \(n^k\) 步內的整個計算過程編碼成一個布林公式 \(\phi_{M,x}\) : - 變數 = 「在第 \(t\) 步,紙帶第 \(i\) 格的符號是 \(\sigma\) 」; - 子句 = 機器的轉移規則(每一步的合法性); 3.

\[ \phi_{M,x} \text{ 可滿足} \Longleftrightarrow M \text{ 接受 } x. \]
  1. 構造 \(\phi\) 只需多項式時間——故 \(L \le_p\) SAT。
\[ \text{所有 NP 問題} \le_p \text{SAT} \quad\text{(SAT 是 NP 的極大元)}. \]

第三條線索:歸約與 NP-完全的傳播(Karp 1972)#

多項式歸約:

\[ A \le_p B: \quad x \mapsto f(x) \text{(多項式時間計算)},\quad x \in A \Longleftrightarrow f(x) \in B. \]

性質:

\[ A \le_p B,\ B \in P \Rightarrow A \in P. \qquad \text{(若 B 可解,A 也可解)}. \]

Karp 的 21 個歸約(1972):

\[ \text{SAT} \le_p \text{3-SAT} \le_p \text{分區} \le_p \text{TSP} \le_p \cdots \]

一個鏈條把 21 個問題全部連到 SAT—— NP-完全的「傳染病」(至今發現數千個 NP-完全問題)。

Python:SAT、3-SAT 與 NP-完全的驗證#

from itertools import product

# 1) SAT:暴力解(2^n,指數時間)
def sat_brute(clauses, n_vars):
    """clauses: 子句列表(每個子句是變數索引列表,正=xi,負=¬xi)"""
    for assign in product([False, True], repeat=n_vars):
        if all(any(assign[abs(l)-1] == (l > 0) for l in clause) for clause in clauses):
            return assign
    return None

# 2) 3-SAT 的例子(3 個文字一個子句)
clauses = [[1, 2, 3], [-1, 2, 3], [1, -2, 3], [-1, -2, -3]]
print("3-SAT 的暴力解(2^n 時間):")
for n_vars in [3, 5]:
    if n_vars == 3:
        c = clauses
    else:
        c = clauses + [[2, 4, 5], [-3, -4, -5], [1, 4, -5], [-2, -5, 4]]
    import time
    import itertools as it
    result = sat_brute(c, n_vars)
    n_combos = 2**n_vars
    print(f"  {n_vars} 個變數,{len(c)} 個子句:{n_combos} 種組合")
    if result:
        print(f"    可滿足!指派 = {[int(v) for v in result]}")
    else:
        print(f"    不可滿足")

# 3) 指數爆炸的驗證(2^n 的成長)
print("\n指數爆炸(2^n 的成長):")
for n in [10, 20, 30, 40, 50]:
    print(f"  n = {n}: 2^n = {2**n:>15} 種組合")
    if n == 30:
        print(f"    (10 億種——已不可行)")
    if n == 50:
        print(f"    (10^15 種——即使每秒 10 億次也要 12 天)")

# 4) 歸約:3-SAT → 頂點覆蓋(Karp 1972 的歸約之一)
print("\n歸約(3-SAT → 頂點覆蓋,Karp 1972):")
# 對每個子句建一個三角形,變數對建一對邊
def reduce_3sat_to_vc(clauses, n_vars):
    """歸約:多項式時間的圖構造"""
    edges = []
    node_id = n_vars   # 子句節點從 n_vars 開始
    clause_nodes = []
    for i, clause in enumerate(clauses):
        a, b, c = node_id, node_id+1, node_id+2
        clause_nodes.append((a, b, c))
        edges += [(a,b), (b,c), (a,c)]   # 三角形
        node_id += 3
    # 變數節點與子句節點的連接
    for i, clause in enumerate(clauses):
        for j, lit in enumerate(clause):
            var = abs(lit) - 1
            edges.append((clause_nodes[i][j], var))
    k = n_vars + len(clauses)   # 覆蓋大小
    return edges, k

edges, k = reduce_3sat_to_vc(clauses, 3)
print(f"  3 個子句 → 圖:{len(edges)} 條邊,覆蓋大小 k = {k}")
print(f"  構造時間 = O(子句數 × 文字數)(多項式 ✓)")
print("  → 3-SAT 可滿足 ⟺ 圖有大小 k 的頂點覆蓋")
print("  → 解決頂點覆蓋就能解決 3-SAT(NP-完全的傳播)")

# 5) P vs NP 的意義
print("\nP vs NP(千禧年問題,1971 Cook):")
print("  P:多項式時間可解(排序、最短路徑)")
print("  NP:多項式時間可驗證(SAT、TSP)")
print("  P ⊆ NP;問題:P = NP?")
print("  若 P = NP:密碼學崩潰(RSA 可破)、最佳化全解")
print("  若 P ≠ NP:有些問題原理上無法快速求解(多數人相信)")
print("  → 這是計算理論最重要的未解問題(100 萬美元獎金)")
輸出:
3-SAT 的暴力解(2^n 時間):
  3 個變數,4 個子句:8 種組合
    可滿足!指派 = [0, 0, 1]
  5 個變數,8 個子句:32 種組合
    可滿足!指派 = [0, 0, 1, 1, 0]

指數爆炸(2^n 的成長):
  n = 10: 2^n =            1024 種組合
  n = 20: 2^n =       1048576 種組合
  n = 30: 2^n =    1073741824 種組合
    (10 億種——已不可行)
  n = 40: 2^n = 1099511627776 種組合
  n = 50: 2^n = 1125899906842624 種組合
    (10^15 種——即使每秒 10 億次也要 12 天)

歸約(3-SAT → 頂點覆蓋,Karp 1972):
  3 個子句 → 圖:24 條邊,覆蓋大小 k = 7
  構造時間 = O(子句數 × 文字數)(多項式 ✓)
  → 3-SAT 可滿足 ⟺ 圖有大小 k 的頂點覆蓋
  → 解決頂點覆蓋就能解決 3-SAT(NP-完全的傳播)

P vs NP(千禧年問題,1971 Cook):
  P:多項式時間可解(排序、最短路徑)
  NP:多項式時間可驗證(SAT、TSP)
  P ⊆ NP;問題:P = NP?
  若 P = NP:密碼學崩潰(RSA 可破)、最佳化全解
  若 P ≠ NP:有些問題原理上無法快速求解(多數人相信)
  → 這是計算理論最重要的未解問題(100 萬美元獎金)

結案 -- 後果與影響#

  • 計算複雜度的分水嶺(1971):
\[ \boxed{\text{P vs NP = 「快 vs 慢」的原理問題}} \]

Cook 的發現把「演算法的分析」(Knuth 1968)推到了極致—— 不只分析「這個演算法多快」,而是問「這個問題原理上能多快」:

\[ \text{演算法分析(1959/1968)} \xrightarrow{\text{Cook(1971)}} \text{複雜度理論(計算的下界)}. \]
  • NP-完全的傳染病(1972–):
\[ \text{Karp 的 21 個(1972)} \to \text{數千個(至今)} \]

NP-完全的問題無所不在: - 物流:TSP、車輛路徑(VRP); - 排程:課表排程、工作排程; - 晶片設計:電路佈局、通道分配; - AI:規劃問題、SAT 求解器; - 生物資訊:蛋白質折疊、基因組比對。

\[ \text{遇到 NP-完全問題的對策}: \begin{cases} \text{近似演算法(approximation)}\\ \text{啟發式(heuristic)}\\ \text{隨機演算法(randomized)}\\ \text{參數化複雜度(FPT)}\end{cases} \]
  • 密碼學的依賴(1976–):
\[ \text{P vs NP} \xrightarrow{\text{Diffie–Hellman(1976)}} \text{公開金鑰密碼學(2002/2015 圖靈獎)} \]

RSA 的安全性基於「質因數分解沒有多項式演算法」—— 若 P = NP,RSA 崩潰—— 整個數位世界的安全依賴於 P ≠ NP(見「2002-RSA公開金鑰.md」)。 - 量子計算的插入(1994–2000): - Shor(1994):質因數分解在量子電腦上有多項式演算法—— RSA 的威脅; - 姚期智(2000 圖靈獎):量子通訊複雜度—— 量子計算的理論基礎(見「2000-姚期智量子計算.md」)。

\[ \text{P vs NP(經典)} \xrightarrow{\text{Shor(1994)}} \text{BQP(量子類別)} \xrightarrow{} \text{後量子密碼學}. \]
  • 千禧年問題(2000):
\[ \boxed{P \text{ vs } NP \text{ = Clay 研究所七大問題之一(100 萬美元)}} \]

至今未解——這是計算理論的「聖杯」: - 多數複雜度學家相信 P ≠ NP(Poll 的調查,2002/2019); - 證明的困難:需要新的數學工具(自然證明障礙、相對化障礙)。 - Cook 的後續(1982 圖靈獎): Cook 1982 年獲圖靈獎——他的演講主題是計算複雜度的展望; 他與 Reckhow 進一步研究了證明系統的複雜度(propositional proof complexity)。 - 歷史定位:Cook 是「計算的邊界」的發現者——

\[ \text{什麼能算(Turing 1936)} \xrightarrow{\text{Cook 1971}} \text{什麼算得快(複雜度)}. \]

一句話:有些問題容易驗證但難求解——這是原理,不是巧合。

關鍵人物與文獻#

  • S. A. Cook:The Complexity of Theorem-Proving Procedures(1971);1982 圖靈獎。
  • L. A. Levin:Universal search problems(1973)——平行發現(Cook–Levin 定理)。
  • R. M. Karp:Reducibility Among Combinatorial Problems(1972)——21 個 NP-完全問題;1985 圖靈獎。
  • A. Cobham:The intrinsic computational difficulty of functions(1964)——多項式時間的定義。
  • P. Shor:量子因數分解(1994)——RSA 的威脅。
  • 相關案件:1936-圖靈機.md、1968-克努特演算法藝術.md、2002-RSA公開金鑰.md、2000-姚期智量子計算.md。