跳轉到

1961 - Hoare 快速排序#

案件摘要#

1961 年,26 歲的 C. A. R. Hoare 發表快速排序(Quicksort):分而治之,用一個樞紐(pivot)把陣列分成兩半。這個演算法最壞情況是 \(O(n^2)\) ——但實務上快得驚人。之後的隨機化分析(隨機選樞紐,期望 \(O(n \log n)\) )成為隨機算法分析的開山經典:第一次用機率論嚴格證明「隨機化的演算法平均上遠優於最壞情況」。

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

1960 年 Hoare 在莫斯科讀統計系統的機器翻譯計畫,需要按字母順序排序大量單字。當時的排序(合併排序需要額外記憶體;泡沫排序太慢)。Hoare 想到一個原地、分而治之的方法:選一個元素當樞紐,把小的放左邊、大的放右邊,再遞迴兩側。

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

演算法#

import random

def quicksort(a, lo=0, hi=None):
    if hi is None: hi = len(a) - 1
    if lo >= hi: return a
    p = partition(a, lo, hi)
    quicksort(a, lo, p - 1)
    quicksort(a, p + 1, hi)
    return a

def partition(a, lo, hi):
    pivot = a[hi]                      # 固定選尾端(最壞情況來源)
    i = lo
    for j in range(lo, hi):
        if a[j] <= pivot:
            a[i], a[j] = a[j], a[i]
            i += 1
    a[i], a[hi] = a[hi], a[i]
    return i

random.seed(42)
print(quicksort([3, 1, 4, 1, 5, 9, 2, 6]))  # [1, 1, 2, 3, 4, 5, 6, 9]

def randomized_quicksort(a, lo=0, hi=None):
    if hi is None: hi = len(a) - 1
    if lo >= hi: return a
    r = random.randint(lo, hi)         # 隨機選樞紐!
    a[r], a[hi] = a[hi], a[r]
    p = partition(a, lo, hi)
    randomized_quicksort(a, lo, p - 1)
    randomized_quicksort(a, p + 1, hi)
    return a

最壞情況#

固定選尾端、輸入已排序:每次分割只有一側,遞迴深度 \(n\) :

\[ T(n) = T(n-1) + \Theta(n) \implies T(n) = \Theta(n^2) \]

隨機化分析:期望 O(n log n)#

隨機選樞紐後,沒有壞輸入,只有壞運氣。關鍵技巧:只追蹤「兩兩比較」的機率。設元素排序後為 \(z_1 < z_2 < \dots < z_n\) ,指示變數:

\[ X_{ij} = [\,z_i \text{ 與 } z_j \text{ 被比較}\,] \]

比較總數 \(X = \sum_{i<j} X_{ij}\) 。 \(z_i, z_j\) 被比較若且唯若兩者中先被選為樞紐(一旦有第三者介入其間,兩者就分開不再比較):

\[ P(X_{ij} = 1) = \frac{2}{j - i + 1} \]

於是:

\[ E[X] = \sum_{i=1}^{n} \sum_{j=i+1}^{n} \frac{2}{j-i+1} = 2\sum_{d=1}^{n-1} \frac{n-d}{d+1} = O(n \log n) \]

(用調和級數 \(\sum 1/d = \ln n + O(1)\) 。)

偵探筆記:這個「指示變數 + 機率求和」的技巧成為隨機算法分析的標準工具——不追蹤整棵遞迴樹,只算每對元素相遇的機率。

高機率界#

不只期望值:隨機化 Quicksort 以機率 \(\ge 1 - n^{-c}\) 在 \(O(c\, n \log n)\) 時間內完成(由 Chernoff 界對遞迴深度的分析)。「壞運氣」發生的機率隨 \(n\) 指數級消失。

結案 -- 後果與影響#

  • 隨機化成為設計策略:Hoare 的分析確立了「隨機化對抗最壞情況輸入」的範式——隨機選樞紐讓敵人無法預測演算法行為。
  • 實務標準:C 語言 qsort、Java Arrays.sort(基本型別)、各種函式庫的排序核心。
  • 選擇問題:Hoare 的隨機選擇(quickselect,1971)期望 \(O(n)\) 找第 \(k\) 小——同一招的延伸。
  • 對手模型:Yao 原理(1985,見 1985-Yao計算隨機性.md)把「隨機化輸入 vs 隨機化演算法」的關係形式化,其思想源頭正是 Quicksort 分析。
  • 平滑分析的先聲:「為什麼實務很快、理論最壞很慘」之謎(Spielman–Teng 2004 最終解決,見 2004-SpielmanTeng平滑分析.md)從 Quicksort 開始發酵。

關鍵人物與文獻#

  • Tony Hoare(1934–2025):Quicksort (1961, Comm. ACM)、quickselect;圖靈獎 1980
  • 交叉參照:1985-Yao計算隨機性.md、1989-Karger最小割.md、2004-SpielmanTeng平滑分析.md