Advanced Algorithms — Under the Hood¶
CMU 15-850 (Anupam Gupta, Fall 2020) · Internal Mechanics¶
Not a tutorial. This document maps how data flows through memory structures, how mathematical invariants propagate, and how algorithmic state evolves at the register/heap level — from MST history through graph matchings, metric embeddings, streaming sketches, and dimension reduction.
1. Minimum Spanning Trees — 100 Years of Internal Optimization¶
1.1 The Cut & Cycle Rules as Invariant Pumps¶
All MST algorithms are ultimately invariant-maintenance engines. Two rules govern what edges can be safely colored:
flowchart TD
subgraph CUT_RULE["Cut Rule (Blue Rule)"]
CR1[Partition V into S and V\\S] --> CR2{Min-weight crossing edge e?}
CR2 --> CR3[Color e BLUE — must be in MST]
CR3 --> CR4[Exchange proof: any tree T without e has\nhigher weight tree T' = T - e' + e where w(e') > w(e)]
end
subgraph CYCLE_RULE["Cycle Rule (Red Rule)"]
RR1[Identify a cycle C in G] --> RR2{Heaviest edge e on C?}
RR2 --> RR3[Color e RED — cannot be in MST]
RR3 --> RR4[Contradiction: removing e' from T and adding e\ngives lower-weight spanning tree]
end
CUT_RULE -->|"Blue edges form\nspanning tree → done"| DONE["MST Complete"]
CYCLE_RULE -->|"Non-red edges\nacyclic → done"| DONE
Key insight: All known deterministic MST algorithms use only the Cut Rule. The Karger-Klein-Tarjan randomized algorithm additionally exploits the Cycle Rule for edge filtering.
1.2 Algorithm Complexity Timeline — Why It Matters Internally¶
timeline
title MST Algorithm Complexity Evolution
1926 : Borůvka O(m log n)
Parallel edge selection per component
1930 : Jarník/Prim O(m log n)
Priority queue frontier expansion
1956 : Kruskal O(m log n)
Sort + Union-Find connectivity
1975 : Yao O(m log log n)
Borůvka + linear median-finding subroutine
1984 : Fredman-Tarjan O(m log* n)
Fibonacci heaps + bounded frontier trick
1995 : Karger-Klein-Tarjan O(m) randomized
Random sampling + cycle rule filtering
1997 : Chazelle O(m·α(n)) deterministic
Soft heaps with controlled corruption
1998 : Pettie-Ramachandran O(MST*(m,n))
Optimal but runtime unknown
1.3 Kruskal's Algorithm — Memory and Data Flow¶
Kruskal processes edges in weight order, maintaining a Union-Find forest in memory:
sequenceDiagram
participant Sort as Edge Sorter
participant UF as Union-Find Forest
participant MST as MST Edge List
Note over Sort: O(m log m) sort pass<br/>edges[] in weight order
loop For each edge e = (u,v) in sorted order
Sort->>UF: find(u), find(v)
UF-->>Sort: root_u, root_v
alt root_u ≠ root_v (different components)
Sort->>MST: add edge e
Sort->>UF: union(root_u, root_v)
Note over UF: Path compression + rank union<br/>amortized O(α(m)) per op
else root_u = root_v (same component)
Note over Sort: Skip — would form cycle<br/>(Cycle Rule implicit)
end
end
Note over MST: n-1 edges collected → MST complete
Union-Find memory layout — path compression collapses chains in-place:
block-beta
columns 5
block:BEFORE["Before path_compress(5)"]:5
A["parent[1]=1\n(root)"]
B["parent[2]=1"]
C["parent[3]=2"]
D["parent[4]=3"]
E["parent[5]=4"]
end
block:AFTER["After find(5) with path compression"]:5
A2["parent[1]=1\n(root)"]
B2["parent[2]=1"]
C2["parent[3]=1\n← compressed"]
D2["parent[4]=1\n← compressed"]
E2["parent[5]=1\n← compressed"]
end
The inverse Ackermann function α(n) represents how many times you can apply log* before reaching 1. For all practical n, α(n) ≤ 4.
1.4 Prim/Jarník — Priority Queue as Frontier Memory¶
Prim maintains a priority queue encoding the cheapest known connection from the growing tree T to each external vertex:
stateDiagram-v2
[*] --> INIT: Insert all V\{r} with key=∞; r with key=0
INIT --> EXTRACT: extractMin() → vertex u with min key
EXTRACT --> GROW: Add u to tree T, add edge (parent[u], u) to MST
GROW --> UPDATE: For each neighbor v of u
UPDATE --> DECREASE: decreaseKey(v, w(u,v)) if w(u,v) < key[v]
DECREASE --> CHECK: All V in T?
CHECK --> DONE: Yes → MST complete
CHECK --> EXTRACT: No → continue
DONE --> [*]
Fibonacci Heap vs Binary Heap — the internal difference:
flowchart LR
subgraph BINARY_HEAP["Binary Heap"]
BH1["insert: O(log n)\nworst case"]
BH2["decreaseKey: O(log n)\nbubble-up sift"]
BH3["extractMin: O(log n)\nheapify-down"]
BH4["Total Prim: O(m log n)"]
end
subgraph FIB_HEAP["Fibonacci Heap"]
FH1["insert: O(1)\namortized — lazy meld"]
FH2["decreaseKey: O(1)\namortized — cut + add to root list"]
FH3["extractMin: O(log H)\namortized — consolidate trees"]
FH4["Total Prim: O(m + n log n)"]
end
BINARY_HEAP -->|"m decreaseKey calls\ndominate"| PERF_B["O(m log n)"]
FIB_HEAP -->|"m O(1) decreaseKeys\n+ n O(log n) extractMins"| PERF_F["O(m + n log n)"]
1.5 Borůvka — Parallel Contraction Rounds¶
Borůvka's algorithm is the MST algorithm most amenable to parallelism:
flowchart TD
G0["G₀ = original graph\nn nodes, m edges"]
G0 --> R1["Round 1: Each node selects\ncheapest outgoing edge"]
R1 --> C1["Contract selected edges\n(guaranteed to form forest\nsince distinct weights)"]
C1 --> G1["G₁: ≤ n/2 super-nodes\n(each node paired with ≥1 other)"]
G1 --> R2["Round 2: Each super-node selects\ncheapest outgoing edge"]
R2 --> C2["Contract again"]
C2 --> G2["G₂: ≤ n/4 super-nodes"]
G2 --> DOTS["... log₂(n) rounds maximum ..."]
DOTS --> SINGLE["Single super-node\n→ MST edges extracted"]
Note1["Each round: O(m) work\nTotal: O(m log n) rounds\nParallel: O(log²n) with PRAM"]
DOTS --- Note1
1.6 Fredman-Tarjan — Bounded-Frontier Trick¶
The breakthrough insight: limit Prim's heap size to K, restart when boundary exceeds threshold:
sequenceDiagram
participant PQ as Fibonacci Heap
participant T as Current Tree
participant FOREST as Blue Forest
Note over PQ,FOREST: Round r, threshold K = 2^(2^r)
loop While unmarked vertices remain
PQ->>T: Start Jarník/Prim from unmarked vertex
loop Grow T
T->>PQ: extractMin() — O(log K) cost
PQ-->>T: vertex u with min key
T->>T: Add u, update N(T) neighbors
alt |N(T)| ≥ K or u was marked
T->>FOREST: STOP — mark all T vertices
Note over FOREST: Save cheapest external edge per node
end
end
end
Note over FOREST: Borůvka contraction step
FOREST->>PQ: Contract forest → new graph
Note over PQ: log*(n) rounds until single node
Key complexity: Each Prim phase has at most K insertions, so heap size ≤ K, so each extractMin costs O(log K). With K phases per round and the Borůvka contraction halving n, total rounds = O(log* n).
2. Graph Matchings — Augmenting Path Mechanics¶
2.1 Berge's Theorem — State Machine View¶
stateDiagram-v2
[*] --> START_MATCHING: M = empty matching
START_MATCHING --> SEARCH: Search for M-augmenting path P
SEARCH --> AUGMENT: Found P → M := M △ P
AUGMENT --> SEARCH: |M| increased by 1
SEARCH --> DONE: No augmenting path exists
DONE --> [*]: M is maximum matching
note right of AUGMENT
Symmetric difference M △ P
toggles path edges:
matched ↔ unmatched
P has odd length (2k+1 edges)
|M| gains exactly 1 edge
end note
Why augmenting paths work — the invariant at the bit level:
flowchart LR
subgraph PATH["Augmenting Path P (length 2k+1)"]
direction LR
O1["●\nopen"] ---U1["───\nunmatched"] --- M1["●\nmatched"] ---MM1["═══\nmatched"] --- M2["●\nmatched"] --- U2["───\nunmatched"] --- O2["●\nopen"]
end
subgraph TOGGLE["After M := M △ P"]
direction LR
O1b["●\nnow matched"] ---U1b["═══\nnow in M"] --- M1b["●\nstill matched"] ---MM1b["───\nnow out of M"] --- M2b["●\nstill matched"] --- U2b["═══\nnow in M"] --- O2b["●\nnow matched"]
end
PATH -->|"XOR edges"| TOGGLE
Note["k+1 new M edges\nk removed M edges\nNet gain: +1"]
TOGGLE --- Note
2.2 Bipartite Matching — Alternating BFS State¶
König's theorem proof gives an alternating BFS that simultaneously finds augmenting paths or constructs minimum vertex covers:
sequenceDiagram
participant L as Left Vertices (L)
participant R as Right Vertices (R)
participant BFS as Alternating BFS Queue
Note over L,BFS: X₀ ← all open (unmatched) vertices in L
BFS->>L: Enqueue all open L vertices at level 0
loop BFS expansion
BFS->>R: X₂ᵢ₊₁ ← neighbors via NON-matching edges
alt Open vertex v found in R
BFS-->>L: AUGMENTING PATH FOUND
Note over BFS: Trace back path, augment M
end
BFS->>L: X₂ᵢ₊₂ ← neighbors via MATCHING edges only
end
Note over L,R: If BFS exhausted without augmenting path:
Note over L: Vertex cover C = (L \ X) ∪ (R ∩ X)
Note over R: |C| = |M| → König's theorem proved
Vertex Cover Construction from BFS layers:
flowchart TD
BFS_TREE["BFS Layer Structure"]
BFS_TREE --> L_MARKED["L ∩ X: marked left vertices\n(reachable from open L via BFS)"]
BFS_TREE --> R_MARKED["R ∩ X: marked right vertices\n(reachable via non-M edges)"]
BFS_TREE --> L_UNMARKED["L \\ X: unmarked left vertices\n(blocked from BFS)"]
BFS_TREE --> R_UNMARKED["R \\ X: unmarked right vertices\n(never reached)"]
L_UNMARKED --> COVER["Vertex Cover C"]
R_MARKED --> COVER
COVER --> PROOF["Proof |C| = |M|:\n• Every R∩X vertex is matched\n (else augmenting path found)\n• Every L\\X vertex is matched\n (else it's open, so it's in X₀)\n• No M edge between L\\X and R∩X\n (else L vertex would be in X)\n→ Bijection: C ↔ M edges"]
2.3 Blossom Algorithm — Handling Odd Cycles¶
General graphs have blossoms (odd cycles) that break the bipartite alternating BFS:
flowchart TD
subgraph BLOSSOM_DETECTION["Odd Cycle Detection"]
V1["Vertex v"] --> E1["Unmatched edge"]
E1 --> V2["Vertex u (already in current alternating tree)"]
V2 --> ODD["v and u at same 'parity' in tree\n→ BFS found ODD cycle = BLOSSOM"]
end
subgraph CONTRACTION["Blossom Contraction"]
B1["Contract blossom B into single super-vertex b"]
B2["Continue augmenting path search on contracted G/B"]
B3["Found augmenting path in G/B → lift to path in G"]
B1 --> B2 --> B3
end
subgraph LIFTING["Path Lifting through Blossom"]
P1["Augmenting path enters blossom at some vertex"]
P2["Route through blossom: alternating edges within B"]
P3["Maintain alternating structure end-to-end"]
P1 --> P2 --> P3
end
BLOSSOM_DETECTION --> CONTRACTION --> LIFTING
Edmonds' Blossom complexity: O(mn²) — each of n augmentations requires O(m) BFS with O(n) blossom contractions.
3. Metric Embeddings — Distortion Mechanics¶
3.1 Low-Stretch Spanning Trees¶
The fundamental question: can we approximate arbitrary metrics by tree metrics?
flowchart LR
subgraph ORIGINAL["Original Metric (G, d)"]
direction TB
N1["n nodes\narbitrary distances"]
N2["d(u,v) = shortest path\nin weighted graph G"]
end
subgraph TREE["Tree Metric (T, d_T)"]
direction TB
T1["Same n nodes\ntree structure only"]
T2["d_T(u,v) = path length\nin spanning tree T"]
end
subgraph QUALITY["Embedding Quality"]
Q1["Stretch(e) = d_T(u,v) / d(u,v)\nfor tree edge e = (u,v)"]
Q2["Average stretch = Σ d(u,v)·stretch(e)\n/ Σ d(u,v)"]
Q3["Goal: low average stretch\nfor all edge pairs"]
end
ORIGINAL -->|"embed into"| TREE
TREE --> QUALITY
Bartal's Probabilistic Tree Embedding:
sequenceDiagram
participant MET as Metric (G, d)
participant ALG as Bartal's Algorithm
participant TREE as Random Tree T
ALG->>MET: Compute diameter Δ of metric
Note over ALG: Pick random root r, random scale β ∈ [Δ/2, Δ]
loop Recursive decomposition
ALG->>MET: Find all vertices within distance β of r
ALG->>TREE: Create cluster C(r) with these vertices
ALG->>ALG: Cut metric at scale β/2
Note over ALG: Recurse on each sub-cluster
Note over ALG: Expected distortion per level: O(log n)
end
Note over TREE: Final tree T with edge weights
Note over ALG: E[d_T(u,v)] ≤ O(log n · log Δ) · d(u,v)
Note over ALG: Bartal (1996): O(log² n) distortion\nFakcharoenphol-Rao-Talwar (2003): O(log n) tight
3.2 Application: Oblivious Routing¶
flowchart TD
subgraph NETWORK["Network G with demands"]
SRC["Source s"] --> |"demand d(s,t)"| SINK["Sink t"]
end
subgraph TREE_ROUTING["Tree-based Oblivious Routing"]
T1["Sample tree T from Bartal distribution"]
T2["Route each (s,t) demand on unique s→t path in T"]
T3["Map tree paths back to edges in G"]
T1 --> T2 --> T3
end
subgraph GUARANTEE["Congestion Guarantee"]
G1["True OPT routing: congestion C*"]
G2["Tree routing congestion: O(log n) · C*"]
G3["Proof: expected distortion O(log n)\n→ expected edge load O(log n) more"]
G1 --> G2 --> G3
end
NETWORK --> TREE_ROUTING --> GUARANTEE
4. Dimension Reduction — JL Lemma Internals¶
4.1 Johnson-Lindenstrauss Transform¶
Goal: Embed n points from ℝᵈ into ℝᵏ (k ≪ d) while preserving pairwise distances.
flowchart LR
subgraph HIGH_DIM["High-dim space ℝᵈ"]
P1["Point x₁ ∈ ℝᵈ"]
P2["Point x₂ ∈ ℝᵈ"]
D1["‖x₁ - x₂‖₂ = D"]
end
subgraph TRANSFORM["Random Projection Matrix A"]
A1["A ∈ ℝᵏˣᵈ, k = O(log n / ε²)"]
A2["Each entry Aᵢⱼ ~ N(0, 1/k)"]
A3["OR: Aᵢⱼ = ±1/√k with equal prob"]
end
subgraph LOW_DIM["Low-dim space ℝᵏ"]
Q1["Ax₁ ∈ ℝᵏ"]
Q2["Ax₂ ∈ ℝᵏ"]
D2["‖Ax₁ - Ax₂‖₂ ∈ [(1-ε)D, (1+ε)D]"]
D3["With probability ≥ 1 - 1/n²"]
end
HIGH_DIM --> TRANSFORM --> LOW_DIM
Why random Gaussian matrices work — moment calculation:
sequenceDiagram
participant VEC as Vector y = Ax (projected)
participant MOMENT as Moment Analysis
participant CONC as Concentration Bound
Note over VEC: y = Ax, want E[‖y‖²] = ‖x‖²
VEC->>MOMENT: E[yᵢ²] = E[(∑ⱼ Aᵢⱼxⱼ)²]
MOMENT->>MOMENT: = ∑ⱼ E[Aᵢⱼ²]·xⱼ² + cross terms
MOMENT->>MOMENT: Cross terms = 0 (independence)
MOMENT->>MOMENT: = ∑ⱼ (1/k)·xⱼ² = ‖x‖²/k
MOMENT->>VEC: E[‖y‖²] = k · ‖x‖²/k = ‖x‖² ✓
VEC->>CONC: Use χ²(k) concentration
CONC->>CONC: P[|‖y‖²/‖x‖² - 1| > ε] ≤ 2exp(-ε²k/4)
CONC->>CONC: Set k = 4 log(n)/ε² for 1/n² failure prob
CONC-->>VEC: Union bound over all C(n,2) pairs → all preserved
4.2 Subgaussian Variables — Memory of Tail Behavior¶
flowchart TD
subgraph GAUSSIANS["Gaussian: X ~ N(0,σ²)"]
G1["E[e^(tX)] = e^(σ²t²/2)"]
G2["Tail: P[X > t] ≤ exp(-t²/2σ²)"]
G3["MGF bounded by Gaussian MGF"]
end
subgraph SUBGAUSSIAN["Subgaussian: X with proxy σ"]
S1["E[e^(tX)] ≤ e^(σ²t²/2) for all t"]
S2["Same tail bound as Gaussian"]
S3["Examples: Bounded r.v. [-a,a],\nRademacher ±1/√k,\nGaussian itself"]
end
subgraph APPLICATION["JL Proof via Subgaussian"]
A1["Aᵢⱼ·xⱼ are independent subgaussian"]
A2["Sum of independent subgaussian\nis subgaussian with σ²=Σσⱼ²"]
A3["yᵢ = ∑ⱼ Aᵢⱼxⱼ is subgaussian"]
A4["Concentration follows from subgaussian tail bound"]
A1 --> A2 --> A3 --> A4
end
SUBGAUSSIAN -->|"yᵢ inherits\nsubgaussian tail"| APPLICATION
5. Streaming Algorithms — Sketching Over Data Streams¶
5.1 Frequency Moment Estimation¶
Process a stream of elements from [n] = {1,...,n}, maintain a small-space sketch to estimate Fₖ = Σᵢ aᵢᵏ (k-th frequency moment):
flowchart LR
subgraph STREAM["Data Stream"]
direction TB
S1["element i₁"] --> S2["element i₂"] --> S3["..."] --> SN["element iₘ"]
end
subgraph SKETCH["AMS Sketch (Alon-Matias-Szegedy)"]
H1["h: [n] → {±1} random hash function"]
Z1["Z = Σₜ h(iₜ) — running sum"]
EST["Estimator: Z² ≈ F₂ = Σᵢ aᵢ²"]
end
subgraph ANALYSIS["Why Z² Works"]
E1["Z = Σᵢ aᵢ·h(i)"]
E2["Z² = (Σᵢ aᵢ·h(i))²"]
E3["E[Z²] = Σᵢ aᵢ² + 2·Σᵢ≠ⱼ aᵢaⱼ·E[h(i)h(j)]"]
E4["E[h(i)h(j)] = 0 (4-wise independence)"]
E5["E[Z²] = Σᵢ aᵢ² = F₂ ✓"]
E1 --> E2 --> E3 --> E4 --> E5
end
STREAM --> SKETCH --> ANALYSIS
Memory layout of streaming sketch:
block-beta
columns 4
block:HASH["Hash Functions\n(seeded PRG)"]:1
H1["h₁: [n]→±1\nO(log n) bits"]
H2["h₂: [n]→±1\nO(log n) bits"]
HT["...t copies..."]
end
block:COUNTERS["Running Sums"]:1
Z1["Z₁ = 0\nupdated each element"]
Z2["Z₂ = 0\nupdated each element"]
ZT["...t counters..."]
end
block:ESTIMATE["Median-of-Means"]:1
M1["Group t into s groups\nMedian of group means"]
M2["Fail prob ≤ δ\nwith s = O(log 1/δ)"]
end
block:TOTAL["Total Space"]:1
SP["O(ε⁻² log 1/δ · log n)\nbits for (ε,δ)-approx of F₂"]
end
5.2 Stream as Vector Model — Turnstile Updates¶
sequenceDiagram
participant STREAM as Stream (i, Δ)
participant VEC as Frequency Vector f ∈ ℤⁿ
participant SKETCH as Sketch Matrix S·f
Note over STREAM: Element arrives: update (i, +1) or (i, -1)
STREAM->>VEC: f[i] += Δ
Note over VEC: Conceptual — too large to store (n entries)
Note over SKETCH: S is random matrix: k×n, k ≪ n
STREAM->>SKETCH: For each update (i,Δ): sketch[j] += S[j,i]·Δ
Note over SKETCH: O(k) work per update, O(k) space total
SKETCH-->>SKETCH: Recover S·f = approximation of f
Note over SKETCH: ‖S·f - f‖ ≤ ε·‖f‖ with high probability
Note over SKETCH: This is compressed sensing / sparse recovery
6. Graph Matchings II — Algebraic Algorithms¶
6.1 Schwartz-Zippel and Polynomial Identity Testing¶
Detecting a perfect matching reduces to determinant computation over a random field:
flowchart TD
subgraph TUTTE_MATRIX["Tutte Matrix Construction"]
TM1["For bipartite G=(L,R,E):\nTutte matrix T where\nT[i,j] = xᵢⱼ if (i,j)∈E, else 0"]
TM2["Xᵢⱼ are distinct indeterminates"]
TM3["Fact: G has perfect matching ⟺ det(T) ≢ 0"]
end
subgraph RANDOMIZATION["Randomized Detection (Schwartz-Zippel)"]
R1["Substitute random values for xᵢⱼ from large field F"]
R2["Compute det(T) numerically: O(n^ω) time"]
R3["If det(T) ≠ 0: perfect matching exists (with high prob)"]
R4["If det(T) = 0: likely no perfect matching"]
R1 --> R2 --> R3
R1 --> R2 --> R4
end
subgraph ERROR["Error Probability"]
E1["By Schwartz-Zippel: P[det=0 | G has PM] ≤ deg(det)/|F|"]
E2["deg(det) ≤ n (one variable per edge per row)"]
E3["Choose |F| = n² → error ≤ 1/n"]
end
TUTTE_MATRIX --> RANDOMIZATION --> ERROR
6.2 Isolation Lemma — Finding a Unique Minimum Witness¶
sequenceDiagram
participant G as Graph G
participant ISO as Isolation Lemma
participant MATCH as Perfect Matching Finder
Note over G: Assign random weights w(e) ∈ [1, 2m] to edges
G->>ISO: Compute minimum weight perfect matching weight W*
Note over ISO: Isolation Lemma: P[unique min-weight PM] ≥ 1/2
ISO-->>MATCH: With prob ≥ 1/2, exactly ONE PM has weight W*
MATCH->>MATCH: For each edge e: remove e, check if min PM weight changes
Note over MATCH: If min weight increases: e is in THE unique min PM
Note over MATCH: O(n) determinant computations → O(n^(ω+1)) total
MATCH-->>G: Return unique minimum perfect matching
7. All-Pairs Shortest Paths — Matrix Multiplication Bridge¶
7.1 Min-Plus Product (Tropical Semiring)¶
APSP reduces to repeated squaring in the min-plus semiring:
flowchart LR
subgraph SEMIRING["Min-Plus Semiring (ℝ, min, +)"]
OPS["Addition: a ⊕ b = min(a,b)\nMultiplication: a ⊗ b = a+b\nIdentity for ⊕: +∞\nIdentity for ⊗: 0"]
end
subgraph MATRIX_POWER["Repeated Squaring"]
D0["D⁰[i,j] = {\n 0 if i=j\n w(i,j) if edge\n +∞ otherwise\n}"]
D1["D¹ = D⁰ ⊗ D⁰\nD¹[i,j] = min over k of (D⁰[i,k] + D⁰[k,j])\n= shortest path using ≤2 edges"]
DK["Dᵏ[i,j] = min over k-hop paths"]
DONE["D^(log n) = exact APSP"]
D0 --> D1 --> DK --> DONE
end
subgraph COMPLEXITY["Complexity Hierarchy"]
C1["Naïve APSP: O(n³)"]
C2["Repeated squaring: O(n³ log n)"]
C3["With fast (min,+) multiply: open problem!"]
C4["Integer weights W: O(n³/log n) or better"]
end
SEMIRING --> MATRIX_POWER
MATRIX_POWER --> COMPLEXITY
7.2 APSP via Fast Matrix Multiplication (Undirected)¶
For undirected graphs with small integer weights, APSP can leverage standard matrix multiplication:
sequenceDiagram
participant G as Graph G (integer weights)
participant ADJ as Adjacency Matrix A
participant MM as Matrix Multiplier (O(n^ω))
participant APSP as APSP Result
Note over G: n nodes, integer edge weights in [1,W]
G->>ADJ: Build n×n adjacency matrix
loop O(log n) iterations
ADJ->>MM: Compute A^k (ordinary matrix power)
MM-->>ADJ: Count paths of length exactly k
Note over MM: A^k[i,j] = # paths of length k from i to j
end
Note over MM: Extract shortest path lengths from path counts
Note over MM: Combines with "witnesses" for path reconstruction
MM->>APSP: D[i,j] for all pairs
Note over APSP: Total: O(n^ω · log² n · log W)
Note over APSP: Beats O(n³) when ω < 3
8. The Ackermann Function and Union-Find — Inverse Tower¶
8.1 Ackermann Tower Internals¶
flowchart TD
subgraph ACKERMANN["Ackermann Function A(m,n)"]
A1["A(1,n) = 2n — doubles n\nA(2,n) = 2ⁿ·n — exponential\nA(3,n) = 2^(2^...(2^n)) n times — tower\nA(k,n) = A(k-1, A(k-1, ..., A(k-1, n)))"]
end
subgraph INVERSE["Inverse Ackermann α(n)"]
AI1["α(n) = min{k : A(k,k) ≥ n}"]
AI2["α(1) = 1, α(2) = 1, α(3) = 2"]
AI3["α(2^65536) = 4 — for all practical n, α(n) ≤ 4"]
end
subgraph UNION_FIND["Why Union-Find is O(mα(m))"]
UF1["Path compression: flatten find() paths\nEach find() amortized O(α(n)) time"]
UF2["Union by rank: trees stay shallow\nDepth bounded by O(log n)"]
UF3["Combined: m operations in O(mα(m)) time\nTarjan's 1975 proof via potential function"]
UF1 --> UF3
UF2 --> UF3
end
ACKERMANN --> INVERSE --> UNION_FIND
9. Concentration of Measure — Probabilistic Algorithm Foundations¶
9.1 Chernoff Bounds — Tail Decay Mechanism¶
flowchart LR
subgraph SETUP["Setup: X = Σ Xᵢ, independent Bernoulli"]
S1["Xᵢ ∈ {0,1}, P[Xᵢ=1] = pᵢ"]
S2["μ = E[X] = Σpᵢ"]
end
subgraph MGF_TRICK["MGF + Markov"]
M1["P[X ≥ (1+δ)μ] = P[e^(tX) ≥ e^(t(1+δ)μ)]"]
M2["≤ E[e^(tX)] / e^(t(1+δ)μ) (Markov)"]
M3["= Πᵢ E[e^(tXᵢ)] / e^(t(1+δ)μ) (independence)"]
M4["E[e^(tXᵢ)] = 1 + pᵢ(eᵗ-1) ≤ exp(pᵢ(eᵗ-1))"]
M5["Πᵢ E[e^(tXᵢ)] ≤ exp(μ(eᵗ-1))"]
M6["Minimize over t: t = ln(1+δ)"]
M7["P[X ≥ (1+δ)μ] ≤ (eᵟ/(1+δ)^(1+δ))^μ"]
M1 --> M2 --> M3 --> M4 --> M5 --> M6 --> M7
end
subgraph SIMPLIFIED["Simplified Bound"]
SB1["P[X ≥ (1+δ)μ] ≤ exp(-δ²μ/3) for δ ≤ 1"]
SB2["P[X ≥ cμ] ≤ (e/c)^(cμ) for c > e"]
end
SETUP --> MGF_TRICK --> SIMPLIFIED
9.2 Power of Two Choices — Load Balancing¶
sequenceDiagram
participant BALLS as n Balls
participant HASH as Two Random Bins
participant BINS as m Bins
loop For each ball
BALLS->>HASH: Pick 2 random bins b₁, b₂
HASH->>BINS: Query load[b₁] and load[b₂]
BINS-->>HASH: Return counts
HASH->>BINS: Place ball in min(load[b₁], load[b₂])
end
Note over BINS: One choice: max load = O(log n / log log n) w.h.p.
Note over BINS: Two choices: max load = O(log log n) w.h.p.
Note over BINS: Exponential improvement from one extra hash query!
Note over BINS: Application: hash tables, load balancers, memory allocation
10. Summary — Algorithmic State Transitions Map¶
flowchart TD
subgraph MST_LAND["MST Algorithms"]
K["Kruskal:\nSort→UF-merge\nO(m log n)"]
P["Prim:\nFibHeap+decreaseKey\nO(m+n log n)"]
B["Borůvka:\nParallel contraction\nO(m log n)"]
FT["Fredman-Tarjan:\nBounded heap+Borůvka\nO(m log* n)"]
KKT["Karger-Klein-Tarjan:\nRandom sampling+cycle filter\nO(m) expected"]
end
subgraph MATCHING_LAND["Matching Algorithms"]
BIOL["Bipartite:\nAlt-BFS\nO(mn)"]
HK["Hopcroft-Karp:\nMulti-path BFS\nO(m√n)"]
BLOSSOM["Blossom:\nOdd-cycle contraction\nO(mn²)"]
ALG["Algebraic:\nTutte det + isolation\nO(n^ω)"]
end
subgraph EMBEDDING_LAND["Embeddings & Dimension"]
BARTAL["Bartal trees:\nO(log² n) distortion"]
JL["JL Lemma:\nO(log n/ε²) dims"]
SK["Streaming sketches:\nO(ε⁻² log n) space"]
end
UF["Union-Find\nO(mα(m))"] --> K
FIBH["Fibonacci Heap\nO(1) amortized ins/decKey"] --> P
UF --> B
FIBH --> FT
B --> KKT
P --> FT
BIOL --> HK
HK --> BLOSSOM
BLOSSOM --> ALG
BARTAL --> JL
JL --> SK
Source: CMU 15-850 Advanced Algorithms, Anupam Gupta (Fall 2020). All diagrams synthesize internal algorithmic mechanics — not reproduced text.