DSA DSA In Depth
0%
lesson np-completenessreductionscomplexity-theory

P, NP, NP-Hardness & Polynomial-Time Reductions (3-SAT ≤p Independent Set ≤p Vertex Cover ≤p Subset Sum)

7 min read Module 6.4: Computational Geometry, Number Theory, Randomized & Approximation Algorithms Updated Oct 23, 2026
📄 Cheat Sheet
P vs. NP and the Computational Complexity Zoo
hackerdashery P vs. NP and the Computational Complexity Zoo Watch on YouTube ↗

Clarifies the direction of Karp reductions (A <=_p B), the Cook-Levin theorem, and the canonical gadget chain from 3-SAT to Independent Set, Vertex Cover, Clique, and Subset Sum.

0:00 / --:--

1. Formal Invariant — Complexity Classes & Karp Reduction Direction

Definition: P, NP, NP-Hard, NP-Complete & $A \le_p B$

  • Class $\mathbf{P}$: Decision problems solvable in deterministic time $O(n^c)$ for some constant $c$.
  • Class $\mathbf{NP}$: Decision problems for which a 'YES' instance $x$ has a polynomial-length certificate $w$ ($|w| = \text{poly}(|x|)$) verifiable by a deterministic algorithm $V(x, w) = 1$ in polynomial time.
  • Karp Reduction ($A \le_p B$): A polynomial-time computable transformation $f$ such that for every instance $x$:

    $$x \in A \iff f(x) \in B$$

  • $\mathbf{NP}$-Hard: Every problem $A \in \mathbf{NP}$ satisfies $A \le_p B$.
  • $\mathbf{NP}$-Complete: $B \in \mathbf{NP}$ AND $B$ is $\mathbf{NP}$-Hard.

2. Karp's Canonical Reduction Chain (1972)

flowchart LR CircuitSAT["Circuit-SAT
(Cook-Levin 1971)"] -->|"Tseitin O(N)"| ThreeSAT["3-SAT
(m Clauses of 3 Literals)"] ThreeSAT -->|"Clause Triangles +
Conflict Edges (K = m)"| IndSet["Independent Set
(|I| >= K)"] IndSet -->|"Complement S = V \ I
(K' = |V| - K)"| VertexCover["Vertex Cover
(|S| <= |V| - K)"] IndSet -->|"Complement Graph G_bar"| Clique["Clique
(|C| >= K)"] VertexCover -->|"Base-4 Incidence Numbers"| SubsetSum["Subset Sum /
0-1 Knapsack"]

3. Anatomy of the Core Reductions

  1. $\text{3-SAT} \le_p \text{Independent Set}$: Given a 3-CNF formula with $m$ clauses, create a triangle of $3$ vertices for each clause (so any independent set can pick at most $1$ vertex per triangle, max $m$ vertices total). Add conflict edges between every literal $x_i$ and its negation $\neg x_i$ across all clauses. Then the graph has an Independent Set of size $K = m$ if and only if one true literal can be chosen from every clause without contradiction!
  2. $\text{Independent Set} \equiv_p \text{Vertex Cover}$ (Gallai's Identity): In any graph $G = (V, E)$, a subset $I \subseteq V$ has no internal edges (Independent Set) if and only if its complement $S = V \setminus I$ touches every edge in $E$ (Vertex Cover)! Thus $\alpha(G) + \tau(G) = |V|$.
  3. $\text{Vertex Cover} \le_p \text{Subset Sum}$: Encode each vertex $v_i$ and edge $e_j$ as digits in a base-4 number with $|E| + 1$ columns (column $0$ has weight $4^{|E|}$ counting chosen vertices, and column $j$ tracks how many endpoints cover edge $e_j$). Adding a slack number $4^{j}$ per edge allows reaching target sum $K \cdot 4^{|E|} + \sum_{j=0}^{|E|-1} 2 \cdot 4^j$ with zero carry between columns iff a Vertex Cover of size $K$ exists!

4. Strong NP-Completeness vs Weak (Pseudo-Polynomial) NP-Completeness

Why $O(N W)$ Knapsack Is NOT a Polynomial-Time Algorithm

In complexity theory, input size is measured in bits: an integer capacity $W$ requires $b = \lceil \log_2 W \rceil$ bits to write down. Therefore, $O(N W) = O(N \cdot 2^b)$ is exponential in the bit-length $b$ (a pseudo-polynomial algorithm)! Problems like Subset Sum, Partition, and 0-1 Knapsack are Weakly NP-Complete (solvable in polynomial time if numbers are given in unary), whereas 3-SAT, Clique, TSP, and 3-Partition remain NP-Complete even when all numbers are polynomial in $N$ (Strongly NP-Complete).

5. Complexity Matrix: Tractable Twins vs NP-Complete Counterparts

Karger Randomized Edge Contraction, 3-SAT Gadget & Knapsack FPTAS
Pr ≥ 2/(n(n-1)) · (1-ε) FPTAS

Simulate Karger random multigraph edge contractions down to 2 supernodes, 3-SAT ≤_p Vertex Cover gadget graphs, and Knapsack FPTAS value-bucket scaling K = εP/n.

Step 1 / 7
Custom Input:
Timeline

Invariant & State

Since min-cut c ≤ 2E/V, a random contraction misses C* with probability ≥ 1 - 2/V. Telescoping over V-2 contractions gives Pr[Min-Cut] ≥ 2 / (V(V-1)).
{A, B, C} {D} 2 Supernodes Remaining ⇒ Cut Value = 2 Parallel Edges
Single Run Success
Pr ≥ 2 / (n(n - 1))
Karger-Stein Fast Cut
O(n² log³ n) w.h.p.
Surviving Cut
({A,B,C}, {D}) = 2 edges
[Step 1] Contracted non-cut edges (A,B) and (B,C); 2 supernodes {A,B,C} and {D} reveal min-cut of size 2.
Tractable Problem in $\mathbf{P}$ Time Complexity NP-Complete Twin Why the Twin Jumps to NP-Complete
2-SAT (2 literals/clause) O(N + M) (Tarjan SCC) 3-SAT (3 literals/clause) $(a \lor b \lor c)$ branches into 2 choices on falsification
Shortest Path (No Neg Cycles) O(M + N log N) Longest Simple Path / Hamiltonian Simple-path non-repeating vertex constraint
Eulerian Circuit (Visit Every Edge) O(N + M) (Hierholzer) Hamiltonian Cycle (Visit Every Vertex) Local degree parity suffices for edges, not vertices
Minimum Cut ($s$-$t$ or Global) O(V^2 E) (Dinic) Maximum Cut (MAX-CUT) Submodular (min) vs Supermodular (max) cut function
Vertex Cover on Bipartite Graphs O(E sqrt(V)) (Kőnig/HK) Vertex Cover on General Graphs Odd cycles break LP integrality gap

6. Production-Grade C++20 Implementation (Karp Reduction $\text{3-SAT} \to \text{Independent Set}$)

#include <vector>
#include <utility>
#include <cmath>

struct Clause3 {
    // Literals encoded as +var (1..num_vars) or -var (-1..-num_vars)
    int lit[3];
};

struct IndependentSetInstance {
    int num_vertices;
    int target_k;
    std::vector<std::pair<int, int>> edges;
};

// Polynomial-time Karp reduction: 3-SAT (m clauses) <=_p Independent Set (3m vertices, K = m)
IndependentSetInstance reduce_3sat_to_independent_set(const std::vector<Clause3>& clauses) {
    const int m = static_cast<int>(clauses.size());
    IndependentSetInstance out;
    out.num_vertices = 3 * m;
    out.target_k = m;

    // 1. Add triangle edges inside each clause c (at most 1 vertex chosen per clause)
    for (int c = 0; c < m; ++c) {
        int u0 = 3 * c, u1 = 3 * c + 1, u2 = 3 * c + 2;
        out.edges.emplace_back(u0, u1);
        out.edges.emplace_back(u1, u2);
        out.edges.emplace_back(u0, u2);
    }

    // 2. Add conflict edges between complementary literals (x and -x)
    for (int c1 = 0; c1 < m; ++c1) {
        for (int i = 0; i < 3; ++i) {
            for (int c2 = c1 + 1; c2 < m; ++c2) {
                for (int j = 0; j < 3; ++j) {
                    if (clauses[c1].lit[i] == -clauses[c2].lit[j]) {
                        out.edges.emplace_back(3 * c1 + i, 3 * c2 + j);
                    }
                }
            }
        }
    }
    return out;
}

Production Pitfall — Reversing the Reduction Direction ($B \le_p \text{3-SAT}$ Instead of $\text{3-SAT} \le_p B$)

The #1 mistake in algorithms interviews and exams is reducing your target problem $B$ into 3-SAT or an ILP solver to prove $B$ is NP-Hard! Showing $B \le_p \text{3-SAT}$ only proves that $B$ is no harder than 3-SAT (which is true even for trivial problems like sorting!). To prove $B$ is NP-Hard, you must reduce a known hard problem FROM $\text{3-SAT} \le_p B$, embedding any arbitrary 3-SAT formula into an instance of $B$.

Check yourself Question 1 of 4
Concept Check Core Invariant Check recall

To prove that a new decision problem $B$ is **NP-Hard**, what polynomial-time reduction must you construct?

⚡ Interactive Challenge · Staff Level

Algorithmic Design & Complexity Check

Analyze the invariant, asymptotic bound, and hardware behavior before selecting.

Why does the $O(N W)$ dynamic programming algorithm for 0-1 Knapsack NOT prove that $\mathbf{P} = \mathbf{NP}$?

Key Takeaways

NP is the class of decision problems with polynomial-time verifiable certificates; NP-Completeness requires both membership in NP and NP-Hardness.
To prove problem B is NP-Hard, you must reduce FROM a known NP-Hard problem A TO B (A <=_p B), never the reverse.
In any graph G = (V, E), I is an Independent Set of size K iff V \ I is a Vertex Cover of size |V| - K, and iff I is a Clique of size K in the complement graph.
Subset Sum and 0-1 Knapsack are Weakly NP-Complete (admitting pseudo-polynomial O(NW) DP), whereas 3-SAT, Clique, and TSP are Strongly NP-Complete.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue