P vs NP &
NP-Completeness

Is finding a solution really harder than checking one?
The biggest open question in computer science, and a US$1,000,000 Clay Millennium Prize.

P

Problems we can solve fast.

NP

Problems whose answers we can check fast.

Reductions

"A is no harder than B." The main tool.

NP-complete

The hardest problems in NP. Solve one, solve all.

Formal definitions, proof sketches and runnable Python. Every timing on these slides comes from a real run (Python 3.11+, Apple Silicon laptop).

Roadmap WHERE WE GO

  1. Why this question matters
  2. Decision vs search problems
  3. Measuring time: the class P, and why growth rate matters
  4. NP as "short, checkable proofs"
  5. Verifiers in Python
  6. NP through nondeterminism
  7. Polynomial-time reductions
  8. NP-hard and NP-complete
  9. SAT and the Cook–Levin theorem
  10. SAT → 3SAT
  11. 3SAT → Independent Set (gadgets)
  12. IS, Clique, Vertex Cover
  13. Worked: CLIQUE is NP-complete
  14. The reduction tree, and a checkpoint
  15. Worked: 3SAT → Subset-Sum
  16. Brute force vs DPLL, measured
  17. Subset-Sum and pseudo-polynomial time
  18. co-NP and the hierarchy map
  19. What if P = NP?
  20. Coping: approximation
  21. Coping: FPT, heuristics, SAT solvers
  22. Ladner's theorem
  23. Why is it so hard to prove? Barriers
  24. Check yourself (two rounds)
  25. Pitfalls, summary, glossary

Why Should You Care? MOTIVATION

A puzzle you already know

A finished Sudoku takes seconds to check: every row, column and box has 1–9.

Filling in a blank n² × n² Sudoku can take far longer. No one knows a fast general method.

P vs NP asks: is that gap real, or just our ignorance?

Where NP-complete problems show up

  • Chip design, hardware and software verification (SAT)
  • Airline crews, class timetables, delivery routes (TSP, coloring)
  • Register allocation in compilers (graph coloring)
  • Protein folding, packing, cutting stock (knapsack)

Why engineers need this

If your problem is NP-complete, stop hunting for a fast exact algorithm. Pick a coping strategy instead.

Garey & Johnson's classic cartoon: "I can't find an efficient algorithm, but neither can all these famous people."

A short history

YearEvent
1956Gödel's letter to von Neumann asks the question
1965Edmonds and Cobham: "efficient = polynomial"
1971Cook: SAT is NP-complete
1972Karp: 21 NP-complete problems
1973Levin, independently, in the USSR
2000Clay Millennium Prize problem

Decision vs Search Problems SETUP

Definition. A decision problem is a language L ⊆ {0,1}*. An instance x is a yes-instance iff x ∈ L.

Search versionDecision version
Find a satisfying assignmentIs φ satisfiable?
Find the shortest tourIs there a tour of length ≤ k?
Find the largest cliqueIs there a clique of size ≥ k?
Find a 3-coloringIs G 3-colorable?

Optimization turns into decision with a budget k. Binary search on k recovers the optimum.

Self-reducibility (SAT). If we can decide SAT in poly time, we can find a satisfying assignment in poly time.

Proof sketch

  1. Ask the oracle: is φ satisfiable? If no, stop.
  2. Set x1 = 1. Ask about φ[x1=1].
  3. If yes, keep it. If no, then φ[x1=0] must be satisfiable.
  4. Repeat for x2, …, xn.

That is n + 1 oracle calls. So decision and search are equally hard for NP-complete problems.

That is why theory can focus on yes/no questions without losing anything.

The Class P DEFINITION

Definition. P = ⋃k DTIME(nk). These are the languages decided by a deterministic Turing machine in O(nk) steps for some constant k. Here n is the input length in bits.

Why polynomial = "efficient"

  • Robust. TMs, RAMs and Python all agree up to a polynomial factor (the extended Church–Turing thesis).
  • Closed. Poly of a poly is a poly. Calling a P subroutine a poly number of times stays in P.
  • Practical. Most natural P problems end up with small exponents.

Caveat: n100 is in P but useless. 1.0001n is not in P but fine for small n.

Problems in P

ProblemAlgorithm
Shortest pathDijkstra, O(m + n log n)
Max flow / bipartite matchingEdmonds–Karp, Hopcroft–Karp
2-SATImplication graph + SCC, linear
Linear programmingEllipsoid (1979), interior point
PRIMESAKS (2002)
2-coloringBFS

Watch the input size

Checking whether N is prime by trial division takes √N steps. The input is only n = log N bits long. So that is 2n/2: exponential.

Why "Polynomial" Is the Line INTUITION

Time to run f(n) steps on a machine doing 109 steps per second:

nn²n³2nn!
10100 ns1 µs1 µs3.6 ms
20400 ns8 µs1 ms77 years
30900 ns27 µs1.1 s8 × 1015 years
502.5 µs125 µs13 days1048 years
10010 µs1 ms4 × 1013 years3 × 10141 years

Buy a 1000× faster computer

  • n²: you can now solve inputs 31× bigger.
  • n³: inputs 10× bigger.
  • 2n: inputs only 10 items bigger (210 ≈ 1000).

Hardware helps polynomial algorithms a lot. It barely helps exponential ones.

Where the brute force comes from

SAT on n variables: try all 2n assignments. TSP on n cities: try all (n−1)!/2 tours. The question "P = NP?" asks: can we always avoid this search?

Common mistake: "exponential" means the exponent grows with n. n50 is polynomial. 2√n is not polynomial, but it is sub-exponential.

NP: Short, Checkable Proofs DEFINITION

Definition (verifier). L ∈ NP iff there is a poly-time algorithm V and a polynomial p such that:

x ∈ L  ⇔  ∃ c, |c| ≤ p(|x|) : V(x, c) = 1

c is the certificate (or witness).

  • Completeness. Every yes-instance has some short proof that V accepts.
  • Soundness. No no-instance has any proof that V accepts.
  • Only yes-answers need proofs. That asymmetry leads to co-NP.

NP stands for Nondeterministic Polynomial time, not "non-polynomial".

ProblemCertificateCheck
SATassignmentevaluate every clause
CLIQUE(k)k verticesall pairs adjacent
HAM-CYCLEvertex ordereach step is an edge
SUBSET-SUMsubsetadd it up
3-COLORcoloringevery edge bichromatic
COMPOSITEa factorone division

P ⊆ NP. If L ∈ P, let V(x, c) ignore c and just decide x.

P = NP?  ⇔  "Can checking always be turned into finding?"

Verifiers in Python CODE

def verify_sat(cnf, assignment):
    """cnf: list of clauses, each a list of non-zero ints (DIMACS style).
    assignment: dict var -> bool. Runs in O(size of formula)."""
    return all(any(assignment[abs(l)] == (l > 0) for l in clause)
               for clause in cnf)

# (x1 v -x2 v x3) ^ (-x1 v x2) ^ (x2 v x3) ^ (-x3 v -x1)
cnf = [[1, -2, 3], [-1, 2], [2, 3], [-3, -1]]
print(verify_sat(cnf, {1: False, 2: True, 3: True}))
print(verify_sat(cnf, {1: True, 2: False, 3: False}))
True
False

Linear time in the formula. Finding that assignment may take up to 2n tries.

import itertools

def verify_clique(edges, k, S):
    S = set(S)
    return len(S) >= k and all(
        (u, v) in edges or (v, u) in edges
        for u, v in itertools.combinations(S, 2))

def verify_subset_sum(nums, target, idx):
    return (len(set(idx)) == len(idx)
            and sum(nums[i] for i in idx) == target)

E = {(1, 2), (1, 3), (2, 3), (3, 4)}
assert verify_clique(E, 3, [1, 2, 3])
assert not verify_clique(E, 3, [2, 3, 4])
assert verify_subset_sum([3, 34, 4, 12, 5, 2], 9, [2, 4])

Each verifier is a few lines, poly time, and needs no cleverness. The hard part is guessing c. Nondeterminism formalizes that guessing.

NP Through Nondeterminism EQUIVALENCE

Definition. NP = ⋃k NTIME(nk). A nondeterministic TM accepts x if some branch of its computation tree accepts within O(nk) steps.

computation tree of an NTM on x reject reject ACCEPT reject depthpoly(n) the path to ACCEPT spells the certificate c up to 2^poly(n) leaves: a deterministic search must explore them

Theorem. Verifier-NP = NTM-NP.

Proof sketch

(⇒) Given verifier V: the NTM guesses c one bit at a time. That takes p(n) branching steps. Then it runs V(x, c) deterministically.

(⇐) Given an NTM: the certificate is the list of choices along an accepting branch. V replays the machine with those choices. That is poly length and poly time.

Simulating deterministically

Try every branch: NTIME(t) ⊆ DTIME(2O(t)). So NP ⊆ EXP. Nobody knows how to do fundamentally better.

Polynomial-Time Reductions KEY TOOL

Definition (Karp / many-one). A ≤p B iff there is a poly-time computable f with

x ∈ A  ⇔  f(x) ∈ B for every x.

instances of A instances of B yes yes nono f (poly time) yes maps to yes, no maps to no

Lemma. If A ≤p B and B ∈ P, then A ∈ P.

Contrapositive. If A is hard, then B is hard too.

Proof. Compute f(x) in p(n) time. So |f(x)| ≤ p(n). Run B's q-time decider on it. Total time: p(n) + q(p(n)), still a polynomial. □

Transitive. A ≤p B ≤p C ⇒ A ≤p C. Just compose the maps.

The #1 student mistake

To show your new problem X is hard, reduce a known hard problem to X: SAT ≤p X.

Reducing X to SAT only shows that X is easy enough to be in NP.

NP-Hard and NP-Complete DEFINITION

NP-hard: B is NP-hard iff A ≤p B for every A ∈ NP.

NP-complete: NP-hard and in NP.

Theorem. If any NP-complete problem is in P, then P = NP.

Proof: every NP problem reduces to it, then use the lemma.

Recipe: prove X is NP-complete

  1. X ∈ NP: give a certificate and a poly-time verifier.
  2. Pick a known NP-complete problem Y.
  3. Build a poly-time map f from Y instances to X instances.
  4. Prove ⇒: a yes for Y gives a yes for X.
  5. Prove ⇐: a yes for X gives a yes for Y.
ProblemIn NP?Status
SAT, 3SAT, CLIQUE, TSP-decisionyesNP-complete
TSP optimization (output the tour)not a languageNP-hard
Halting problemno (undecidable)NP-hard
Generalized chess on n×nno (EXP-complete)NP-hard
2-SAT, 2-COLORyesin P
Graph isomorphism, factoringyesunknown, probably neither

NP-hard means "at least as hard as NP". It can be far harder, even undecidable.

Halting is NP-hard: map φ to a program that tries all assignments and halts iff it finds one.

The Cook–Levin Theorem THEOREM

Theorem (Cook 1971, Levin 1973). SAT is NP-complete.

SAT ∈ NP: the assignment is the certificate. Hardness is the real work. Take any L ∈ NP with an NTM M running in T = nk steps. Build a formula φx that is satisfiable iff M accepts x.

The tableau

Write the run as a T × T grid. Row i is the configuration (tape, head, state) at time i.

Variables: xi,j,s = "cell (i, j) holds symbol s". Here s ranges over tape symbols and (state, symbol) pairs.

t=0t=1…t=T # q0 x1 x2 … xn _ _ _ # 2×3window … q_accept …

φx = φcell ∧ φstart ∧ φmove ∧ φaccept

PartSaysSize
φcelleach cell holds exactly one symbolO(T²)
φstartrow 0 is the start config on xO(T)
φmoveevery 2×3 window is legal for δO(T²)
φacceptqaccept appears somewhereO(T²)

Key insight: locality

A TM step changes only cells next to the head. So "the whole run is legal" becomes "every small window is legal". Each window check is a constant-size formula.

Total size is O(T²) = O(n2k), so the map is polynomial. Satisfying assignments match accepting runs one to one. □

SAT → 3SAT REDUCTION

3SAT: a CNF formula where every clause has exactly 3 literals. Is it satisfiable?

First turn any formula into CNF in poly time with Tseitin encoding (a new variable per gate). Then split each clause into 3-literal clauses:

Clause sizeReplace with
1: (a)(a∨y∨z)(a∨y∨¬z)(a∨¬y∨z)(a∨¬y∨¬z)
2: (a∨b)(a∨b∨y)(a∨b∨¬y)
3keep it
k > 3chain with k−3 new variables (right)

(ℓ1∨ℓ2∨…∨ℓk)  ↦

(ℓ1∨ℓ2∨y1)(¬y1∨ℓ3∨y2)…(¬yk−3∨ℓk−1∨ℓk)

Why it is equisatisfiable

⇒ Say ℓi is true. Set yj = 1 for chain links left of ℓi and 0 to the right. Every link is satisfied.

⇐ Say every ℓi is false. The first clause forces y1 and each link passes it on, yj ⇒ yj+1. The last clause needs ¬yk−3: a contradiction.

But 2SAT is in P

Each clause (a∨b) gives implications ¬a⇒b and ¬b⇒a. It is unsatisfiable iff some x and ¬x share a strongly connected component. That runs in linear time.

The jump from 2 to 3 is where hardness appears. Coloring has the same cliff between 2 and 3 colors.

3SAT → Independent Set: Gadgets REDUCTION

φ = (x1∨x2∨¬x3)(¬x1∨x3∨x2)(¬x2∨¬x3∨x1) x1 x2 ¬x3 ¬x1 x3 x2 x1 ¬x2 ¬x3 clause 1 clause 2 clause 3 triangle: pick at most 1 literal per clause conflict: x and ¬x never both chosen an independent set of size 3 (x1 = x2 = 1)

Map: for a 3-CNF φ with m clauses, build graph G:

  • One vertex per literal occurrence: 3m vertices.
  • A triangle on each clause's three vertices.
  • A conflict edge between every x and ¬x.
  • Ask: does G have an independent set of size k = m?

Correctness

⇒ Take a satisfying assignment. Pick one true literal in each clause. No two are in one triangle. No two conflict, since both are true. So it is an IS of size m.

⇐ An IS of size m has exactly one vertex per triangle. It has no x together with ¬x. Set those literals true and the rest arbitrarily. Every clause is satisfied. □

Map size is O(m²) edges at most. So it runs in poly time.

The Reduction, Tested CODE

def sat3_to_is(cnf):
    """One vertex per literal occurrence. Triangle inside each clause,
    edge between complementary literals. Satisfiable <=> IS of size m."""
    V = [(ci, l) for ci, c in enumerate(cnf) for l in c]
    E = set()
    for a, b in itertools.combinations(V, 2):
        same_clause = a[0] == b[0]
        conflict = a[1] == -b[1]
        if same_clause or conflict:
            E.add((a, b))
    return V, E, len(cnf)

f = [[1, 2, -3], [-1, 3, 2], [-2, -3, 1]]
V, E, k = sat3_to_is(f)
print(len(V), "vertices,", len(E), "edges, need IS of size", k,
      "->", has_is(V, E, k))
9 vertices, 15 edges, need IS of size 3 -> True
def has_is(V, E, k):          # brute force, only for testing
    adj = {v: set() for v in V}
    for a, b in E: adj[a].add(b); adj[b].add(a)
    for S in itertools.combinations(V, k):
        if all(b not in adj[a]
               for a, b in itertools.combinations(S, 2)):
            return True
    return False

# property test: reduction agrees with brute-force SAT
for _ in range(60):
    n = rng.randint(3, 5)
    f = random_3sat(n, rng.randint(2, 6), rng)
    V, E, k = sat3_to_is(f)
    assert has_is(V, E, k) == (brute_sat(f, n) is not None)

9 triangle edges + 6 conflict edges = 15. The test also passes on 60 random formulas, both satisfiable and not. Always test reductions in both directions.

Independent Set, Clique, Vertex Cover ONE PROBLEM, 3 FACES

Independent Set

A set S with no edges inside it. Is there one with |S| ≥ k?

Clique

A set S with all edges inside it. Is there one with |S| ≥ k?

Vertex Cover

A set C touching every edge. Is there one with |C| ≤ k?

IS ⇔ Clique. S is independent in G iff S is a clique in the complement Ḡ.

(G, k) ↦ (Ḡ, k)

IS ⇔ VC. S is independent iff V − S is a vertex cover.

(G, k) ↦ (G, n − k)

Proof (VC). An edge uv is uncovered by V−S iff both ends are in S. That is iff S is not independent. □

a b c d e f IS = {a, d, f} VC = {b, c, e} = V − IS

These maps are trivial, but hardness flows through them. One NP-complete member makes all three NP-complete.

Worked: CLIQUE Is NP-Complete, Start to Finish RECIPE IN USE

Step 1. CLIQUE ∈ NP. The certificate is the k vertices. Check all C(k,2) pairs: O(k²) time.

Step 2. Pick a known NP-complete problem. IS, proved from 3SAT a few slides back.

Step 3. Build the map. f(G, k) = (Ḡ, k). Flip every pair: edge ↔ non-edge. O(n²) time.

Step 4. Yes ⇒ yes. If S is an IS in G, no pair in S is an edge of G. So every pair is an edge of Ḡ. S is a clique in Ḡ.

Step 5. Yes ⇐ yes. Same argument backwards. A clique in Ḡ is an IS in G.

Step 6. Conclude. IS ≤p CLIQUE, and CLIQUE ∈ NP. So CLIQUE is NP-complete. □

def verify_clique(adj, k, S):             # certificate S: the k vertices
    return len(set(S)) >= k and all(v in adj[u] for u, v in combinations(S, 2))

def is_to_clique(adj, k):                 # (G, k) -> (complement of G, k)
    V = list(adj)
    comp = {u: {v for v in V if v != u and v not in adj[u]} for u in V}
    return comp, k

# G = the 6-vertex graph from the last slide
H, k = is_to_clique(G, 3)
print("clique {a,d,f} in complement:", verify_clique(H, 3, "adf"))
print("clique {a,b,c} in complement:", verify_clique(H, 3, "abc"))
clique {a,d,f} in complement: True
clique {a,b,c} in complement: False

IS {a, d, f} in G became a clique in Ḡ. G has 7 edges, so Ḡ has 15 − 7 = 8.

Common mistake: the arrow

Mapping CLIQUE to IS shows CLIQUE is no harder than IS. That proves nothing new. To show CLIQUE is hard, map the known-hard problem into CLIQUE.

The Reduction Tree KARP 1972

Any L in NP CIRCUIT-SAT / SAT 3SAT INDEPENDENT SET CLIQUE VERTEX COVER SET COVER 3-COLOR REGISTER ALLOC DIR. HAM-CYCLE HAM-CYCLE TSP SUBSET-SUM KNAPSACK PARTITION ILP (0-1 programs) Cook–Levin

Arrow A → B means A ≤p B. Hardness flows downward. Every problem shown is NP-complete (the decision versions). Karp's 1972 paper had 21 of them. Garey & Johnson (1979) list over 300.

Checkpoint: What We Know So Far RECAP

The story in five lines

  1. P: we can find the answer fast.
  2. NP: we can check a proposed answer fast.
  3. A ≤p B: a fast solver for B gives a fast solver for A.
  4. NP-complete: in NP, and everything in NP reduces to it.
  5. Cook–Levin gave the first one (SAT). Every other one comes from a chain of reductions.

Memory trick for the arrow

"A ≤ B" reads "A is at most as hard as B". Hardness flows right: if A is hard, B is hard. Easiness flows left: if B is easy, A is easy.

ReductionGadget idea
SAT → 3SATsplit long clauses with new variables
3SAT → IStriangle per clause, conflict edges
IS → CLIQUEcomplement the graph
IS → VCtake V − S

Quick self-test

Say out loud why a size-m IS in the gadget graph must use exactly one vertex per triangle. If you can, you understand gadgets.

(Answer: the triangles allow at most one each, and there are only m triangles.)

Three More Reductions, in Brief GADGETS

3SAT → 3-COLOR

  • A palette triangle: T, F, B (base).
  • For each variable, a triangle x, ¬x, B. So one literal is T and the other F.
  • For each clause, an OR gadget of 6 nodes. It is 3-colorable iff at least one input is T.

Planar 3-coloring is still NP-complete. 4-coloring a planar graph is always possible (4-color theorem).

3SAT → HAM-CYCLE

  • Each variable is a row of nodes walked left-to-right (true) or right-to-left (false).
  • Each clause is an extra node. You can detour into it only from a row whose direction satisfies it.
  • A Hamiltonian cycle picks directions and visits every clause node: a satisfying assignment.

HAM-CYCLE → TSP: weight 1 on edges, 2 on non-edges. Ask for a tour of cost ≤ n.

3SAT → SUBSET-SUM

  • Write numbers in base 10. There is one digit column per variable and one per clause.
  • Numbers vi and v'i put a 1 in column xi. They also put a 1 in every clause column where xi (or ¬xi) appears.
  • Slack numbers pad each clause column. Target: 1 for each variable, 4 for each clause.

Digits never carry (max column sum 3 + 1 + 2 = 6 < 10), so columns act independently.

Pattern. A choice gadget encodes each variable's value. A check gadget tests each clause. Wiring makes them agree. Almost every NP-completeness proof follows this plan.

Worked: 3SAT → Subset-Sum on a Real Formula FULL INSTANCE

φ = C1 ∧ C2, with C1 = (x1 ∨ ¬x2 ∨ x3) and C2 = (¬x1 ∨ x2 ∨ x3).

numberx1x2x3C1C2meaning
v110010x1 true
v1'10001x1 false
v201001x2 true
v2'01010x2 false
v300111x3 true
v3'00100x3 false
s1, s1'0001, 20slack for C1
s2, s2'00001, 2slack for C2
target11144= 11144

Row vi has a 1 in clause column j iff literal xi is in Cj. Row vi' does the same for ¬xi.

Assignment x1 = 1, x2 = 1, x3 = 0

Pick v1, v2, v3':

  10010   v1
+ 01001   v2
+ 00100   v3'
= 11111   each clause has 1 true literal
+ 00033   s1 + s1' + s2 + s2'
= 11144   target hit

Why it works

  • Variable columns must be 1. So pick exactly one of vi, vi': a truth value.
  • Slacks add at most 3 to a clause column. To reach 4, at least one literal must be true.
subsets hitting target: 6   satisfying assignments: 6

Checked by brute force in Python. Common mistake: using only one slack per clause. Then a clause with 1 true literal can only reach 2, and the map breaks.

Solving SAT: Brute Force vs DPLL CODE

def brute_sat(cnf, n):
    for bits in itertools.product([False, True], repeat=n):
        a = {i + 1: bits[i] for i in range(n)}
        if verify_sat(cnf, a):
            return a
    return None
def simplify(cnf, assign):
    out = []
    for c in cnf:
        if any(assign.get(abs(l)) == (l > 0) for l in c):
            continue                             # clause already true
        rest = [l for l in c if abs(l) not in assign]
        if not rest: return None                 # clause false: conflict
        out.append(rest)
    return out
def dpll(cnf, assign=None):
    assign = dict(assign or {})
    while True:                               # unit propagation
        cnf = simplify(cnf, assign)
        if cnf is None: return None
        if not cnf: return assign
        unit = next((c[0] for c in cnf if len(c) == 1), None)
        if unit is None: break
        assign[abs(unit)] = unit > 0
    lit = cnf[0][0]                           # branch
    for val in (lit > 0, lit < 0):
        res = dpll(cnf, {**assign, abs(lit): val})
        if res is not None: return res
    return None

Random 3-SAT with m = 4.26n clauses (the hardest ratio). Mean of 3 formulas per size:

n   brute(s)   dpll(s)
10     0.0023    0.0002
14     0.0263    0.0004
18     0.8263    0.0016
20     1.9263    0.0018
  • Brute force tries up to 2n assignments. It grows about 1000× from n=10 to n=20.
  • Unit propagation spots forced values and prunes huge subtrees. DPLL is about 1000× faster at n=20 here.
  • DPLL is still exponential in the worst case. Pigeonhole formulas need 2Ω(n) steps for any DPLL run (Haken 1985).

Cross-checked: brute force and DPLL agree on 300 random formulas, and every DPLL answer passes verify_sat.

Industrial SAT Solvers PRACTICE

CDCL = DPLL + learning

  1. Decide: pick a variable. The VSIDS heuristic favors ones in recent conflicts.
  2. Propagate: unit propagation, made fast with two watched literals.
  3. Conflict: trace the implication graph back to a cut. Learn a new clause that blocks this mistake forever.
  4. Backjump several levels at once, not just one.
  5. Restart often, keeping the learned clauses.

MiniSat, Glucose, CaDiCaL, Kissat. They routinely solve industrial formulas with millions of variables.

clause/variable ratio m/n solver effort ≈ 4.26 effort P(satisfiable) easy: many solutions easy: quick refutation

The paradox

SAT is NP-complete, yet solvers handle huge real inputs. Real formulas have structure: small backdoors, modularity and low treewidth. Worst-case hardness says nothing about your instance.

Uses: hardware model checking (Intel, AMD), package managers (conda, apt), Amazon's policy checker Zelkova, and proofs like the Boolean Pythagorean triples (2016, 200 TB).

Subset-Sum and Pseudo-Polynomial Time SUBTLETY

def subset_sum(nums, T):
    reach = 1                       # bit i set <=> sum i reachable
    for x in nums:
        reach |= reach << x         # take x, or don't
    return bool(reach >> T & 1)

print(subset_sum([3, 34, 4, 12, 5, 2], 9),
      subset_sum([3, 34, 4, 12, 5, 2], 30))
True False

This is the classic DP table packed into one Python big-int. It takes O(n·T) bit operations.

So is P = NP? No.

T is written in log T bits. So O(nT) = O(n·2log T), which is exponential in the input size.

That is pseudo-polynomial: polynomial in the value of the numbers, not their length.

KindExampleFast if numbers are small?
weakly NP-completeSUBSET-SUM, KNAPSACK, PARTITIONyes (DP)
strongly NP-complete3-PARTITION, bin packing, TSPno, still hard in unary

Weakly NP-complete problems often have an FPTAS (a fully polynomial approximation scheme). Knapsack can be solved to within (1−ε) in poly(n, 1/ε) time.

Check Yourself (1) EXERCISES

Try each one before you open the answer.

1. A surprising reduction

Someone proves 3SAT ≤p 2SAT. What follows?

Answer

P = NP. 2SAT is in P. So 3SAT is in P. 3SAT is NP-complete, so all of NP is in P.

2. Shortest or longest?

Is "is there an s–t path with at most k edges?" NP-complete? What about "at least k edges, no repeats"?

Answer

The first is in P (BFS). The second is NP-complete: with k = n−1 it is Hamiltonian path. One word flips the difficulty.

3. Knapsack DP

Knapsack has an O(nW) DP. Does that put an NP-complete problem in P?

Answer

No. W takes only log W bits to write. So nW = n · 2bits is exponential in the input size. It is pseudo-polynomial.

4. Wrong-way reduction

You reduce your new problem X to SAT. What have you shown?

Answer

Only that X is no harder than SAT. So X is in NP, and a SAT solver can solve X. You have not shown X is hard.

co-NP, PSPACE, EXP: The Map HIERARCHY

EXP PSPACE NP co-NP P SAT, CLIQUE(NP-complete) TAUTOLOGYUNSAT FACTORING?GRAPH ISO? QBF, generalized Go (PSPACE-complete) n×n chess (EXP-complete)

This drawing assumes every inclusion is strict. Only P ≠ EXP is proven (time hierarchy theorem).

co-NP = { L : Л ∈ NP }, the complements of NP languages. No-instances have short proofs.

  • UNSAT: "φ has no satisfying assignment." What short proof shows that? None is known.
  • Factoring is in NP ∩ co-NP. The prime factorization certifies either answer, and PRIMES is in P.
  • If an NP-complete problem is in co-NP, then NP = co-NP.
  • P = NP ⇒ NP = co-NP, since P is closed under complement.
KnownOpen
P ⊆ NP ⊆ PSPACE ⊆ EXPP vs NP
P ≠ EXPNP vs co-NP
PSPACE = NPSPACE (Savitch)P vs PSPACE

At least one inclusion in P ⊆ NP ⊆ PSPACE ⊆ EXP is strict. We don't know which one.

What If P = NP? THOUGHT EXPERIMENT

Cryptography breaks

  • "Is there a key k with Enck(m) = c?" is in NP.
  • One-way functions could not exist. RSA, elliptic curves, AES and SHA would all be invertible in poly time.
  • Proof-of-work (Bitcoin) becomes trivial.

Optimization becomes easy

  • Optimal schedules, routes, chip layouts and protein designs.
  • Learning: find the smallest circuit that fits the data. That is Occam's razor, made automatic.

Math gets automated

"Does theorem T have a proof of at most n symbols?" is in NP. With a fast algorithm, finding proofs would cost about as much as checking them.

Gödel saw this in his 1956 letter. Aaronson: "Everyone who could appreciate a symphony would be Mozart."

Reality checks

  • A proof of P = NP might be non-constructive, or n1000. It could change nothing in practice.
  • A proof of P ≠ NP would not prove crypto safe. Crypto needs average-case hardness, which is a stronger claim.
  • Most experts bet P ≠ NP. Gasarch's 2019 poll: 88% for P ≠ NP.

Impagliazzo's "five worlds" (1995): Algorithmica, Heuristica, Pessiland, Minicrypt and Cryptomania. They describe what the world looks like under each answer.

Coping 1: Approximation VERTEX COVER

def vc_2approx(edges):
    cover = set()
    for u, v in edges:
        if u not in cover and v not in cover:
            cover |= {u, v}        # edge uncovered: take both ends
    return cover

# 200 random graphs on 8 nodes vs brute-force optimum
worst = 0
for _ in range(200):
    edges = [(u, v) for u, v in itertools.combinations(range(8), 2)
             if rng.random() < .3]
    a, o = vc_2approx(edges), vc_opt(range(8), edges)
    assert len(a) <= 2 * len(o)
    if o: worst = max(worst, len(a) / len(o))
print("worst ratio seen:", round(worst, 2))
worst ratio seen: 2.0

The bound is tight. A single edge is enough: ALG picks 2 vertices, while OPT needs only 1.

Theorem. vc_2approx returns a vertex cover with |C| ≤ 2·OPT.

Proof

Let M be the edges where we added both ends. No two edges in M share a vertex, so M is a matching.

Valid: any edge not in M already had a covered end when we reached it.

Bound: any cover must touch each edge of M. The edges in M share no vertex. So OPT ≥ |M|. We output |C| = 2|M| ≤ 2·OPT. □

How good can approximation get?

  • PCP theorem (1992): for some ε, even a (1+ε)-approximation of MAX-3SAT is NP-hard.
  • MAX-3SAT: a random assignment gets 7/8. Doing better is NP-hard (Håstad 2001).
  • Vertex cover: below 2 is hard assuming the Unique Games Conjecture.
  • Metric TSP: Christofides gets 1.5 (1976). Karlin–Klein–Oveis Gharan (2020) beat it by a tiny amount. General TSP cannot be approximated at all unless P = NP.

Coping 2: FPT, Special Cases, Heuristics TOOLKIT

Fixed-parameter tractable (FPT)

Run time f(k)·poly(n). The exponential part depends only on a small parameter k.

VC in O(2k·m): pick any edge uv. Some cover must contain u or v. Branch on both with k−1. The tree has depth k, so at most 2k leaves.

The best known bound is about 1.2738k. By contrast, CLIQUE is W[1]-hard, so it is probably not FPT.

Exploit structure

  • Trees and low treewidth: DP solves IS, VC and coloring in linear time (Courcelle's theorem).
  • Bipartite graphs: VC = max matching (Kőnig), so it is in P.
  • Planar graphs: subexponential 2O(√n) algorithms.

Exact but exponential, done well

  • Branch & bound and ILP solvers (Gurobi, CPLEX, HiGHS).
  • Held–Karp TSP DP: O(n²2n) instead of n!.
  • Concorde solved an 85,900-city TSP to proven optimality (2006).
  • SAT / SMT / CP solvers: encode, then let the solver work.

Heuristics (no guarantee)

  • Local search: 2-opt and Lin–Kernighan for TSP. WalkSAT for SAT.
  • Simulated annealing, tabu search, genetic algorithms.
  • Learned heuristics: GNN branching policies in ILP solvers.

Decision rule. Small n: go exact. Small parameter: FPT. Need a guarantee: approximate. Huge and messy: heuristics or a solver.

Ladner's Theorem: NP-Intermediate THEOREM

Theorem (Ladner 1975). If P ≠ NP, then some language in NP is neither in P nor NP-complete.

Proof idea: "blowing holes in SAT"

Define L = { x ∈ SAT : H(|x|) is even }. Here H is a slow-growing function built by diagonalization:

  • While L looks like SAT, it kills the i-th poly-time machine as a decider for L.
  • While L looks empty (easy), it kills the i-th poly-time reduction from SAT to L.
  • H moves to stage i+1 only after stage i's machine has visibly failed.

So L is never in P and never NP-complete. Stages last long enough for each witness to be found in poly time. □

Natural candidates

ProblemBest known
Integer factoringGNFS, exp(O(n1/3(log n)2/3))
Graph isomorphismBabai 2015: quasi-poly 2O(log n)c
Discrete logsubexponential
Min circuit size (MCSP)unknown

Ladner's language is artificial. Whether any natural problem is NP-intermediate is itself open.

Why factoring is probably not NP-complete

Factoring is in NP ∩ co-NP. If it were NP-complete, then NP = co-NP, which experts doubt.

Shor's algorithm factors in poly time on a quantum computer. No known quantum algorithm solves NP-complete problems in poly time.

Why Is It So Hard to Prove? BARRIERS

Three theorems show that whole families of proof methods cannot settle P vs NP.

1. Relativization

Baker–Gill–Solovay, 1975

There are oracles A and B with PA = NPA and PB ≠ NPB.

Diagonalization and simulation work the same with any oracle attached. So they alone cannot decide the question.

Example A: a PSPACE-complete oracle, such as TQBF.

2. Natural proofs

Razborov–Rudich, 1994

Most circuit lower bounds find a simple property that random functions have and easy functions lack.

If one-way functions exist, such a "natural" property would also break pseudorandom generators.

So the method that proved lower bounds for weak circuit classes (parity ∉ AC0) likely cannot reach P/poly.

3. Algebrization

Aaronson–Wigderson, 2008

Arithmetization got past relativization. It proved IP = PSPACE.

But those methods still "algebrize". There are algebraic oracles on both sides of P vs NP too.

What we can prove

  • Time hierarchy: P ≠ EXP.
  • Circuit lower bounds for restricted models: AC0, monotone circuits (Razborov 1985).
  • Williams 2011: NEXP ⊄ ACC0. It evades all three barriers.

Current programs

  • Geometric complexity theory (Mulmuley–Sohoni): algebraic geometry and representation theory.
  • Proof complexity: lower bounds for stronger proof systems.
  • Hardness magnification and meta-complexity (MCSP).

Check Yourself (2) EXERCISES

5. Can NP be solved at all?

Is every NP problem decidable? How fast, at worst?

Answer

Yes. Try every certificate of length p(n) and run the verifier. That takes 2p(n) · poly time and poly space. So NP ⊆ PSPACE ⊆ EXP.

6. Complements

Show: if P = NP, then NP = co-NP.

Answer

P is closed under complement: flip the answer. If L ∈ co-NP, then ¬L ∈ NP = P. So L ∈ P = NP. The other inclusion is the same.

7. Tautologies

TAUT = formulas true under every assignment. Is TAUT in NP?

Answer

TAUT is co-NP-complete. A "no" has a short proof (one falsifying assignment). A "yes" seems to need all 2n. TAUT is in NP iff NP = co-NP, which most believe is false.

8. Factoring

RSA relies on factoring being hard. Is factoring NP-complete?

Answer

Not known, and probably not. Its decision version is in NP ∩ co-NP. If it were NP-complete, then NP = co-NP. It may be NP-intermediate (Ladner).

Common Pitfalls WATCH OUT

"NP means not polynomial"

No. NP means nondeterministic polynomial. Every P problem is in NP.

Reducing the wrong way

To show X is hard, map a known hard problem to X. The map must run in poly time and keep yes/no both ways.

Proving only one direction

You need both "yes gives yes" and "yes back gives yes". Test your reduction with brute force on small cases.

Forgetting "X ∈ NP"

NP-hard plus a verifier gives NP-complete. Without the verifier you only have NP-hard.

Measuring numbers by value

Input size is bits. An O(nT) DP is pseudo-polynomial, not polynomial.

"NP-hard means hopeless"

Worst case is not your case. SAT and ILP solvers crush many real instances. Approximation and FPT often work.

"My heuristic solved 1000 cases, so P = NP"

A claim needs a proof for every input. Hundreds of claimed proofs have failed (see Woeginger's list).

Confusing optimization and decision

Optimization versions are NP-hard, not NP-complete, because they are not languages.

Summary TAKEAWAYS

ConceptOne line
Psolvable in poly time
NPyes-answers have poly-checkable certificates
co-NPno-answers have poly-checkable certificates
≤ppoly map keeping yes/no: "A no harder than B"
NP-hardall of NP reduces to it
NP-completeNP-hard and in NP
Cook–LevinSAT is NP-complete (tableau + locality)
LadnerP ≠ NP ⇒ NP-intermediate problems exist
Barriersrelativization, natural proofs, algebrization

Five things to remember

  1. Checking vs finding is the whole question.
  2. Reduce from a known hard problem to yours.
  3. Gadgets: choice, check, wiring.
  4. NP-complete in theory can be easy in practice. Try a SAT/ILP solver first.
  5. If exact is out of reach: approximate, parameterize, or use heuristics.

Further reading

  • Sipser, Introduction to the Theory of Computation, ch. 7
  • Garey & Johnson, Computers and Intractability (1979)
  • Arora & Barak, Computational Complexity: A Modern Approach
  • Fortnow, The Golden Ticket (2013), for a general audience
  • Aaronson, "P =? NP" survey (2016)

Glossary REFERENCE

TermMeaning
Decision problemA question with a yes/no answer, seen as a language.
PDecidable in polynomial time by a normal (deterministic) machine.
NPYes-answers have short certificates that can be checked in poly time.
co-NPNo-answers have short, checkable certificates.
Certificate / witnessThe short proof a verifier checks (an assignment, a tour, a set).
VerifierA poly-time program V(x, c) that checks a certificate.
A ≤p BA poly-time map turning A instances into B instances, keeping yes/no.
NP-hardEvery NP problem reduces to it. Need not be in NP.
NP-completeNP-hard and in NP. The hardest problems in NP.
TermMeaning
SAT / 3SATIs a CNF formula satisfiable? 3SAT: each clause has 3 literals.
GadgetA small piece of the output instance that encodes one choice or check.
Pseudo-polynomialPolynomial in the value of numbers, not in their bits.
Strongly NP-hardStill hard when numbers are small (written in unary).
Approximation ratioWorst-case ALG / OPT (or OPT / ALG for maximizing).
PCP theoremNP proofs can be checked by reading a few random bits.
FPTTime f(k) · poly(n): exponential only in a parameter k.
NP-intermediateIn NP, not in P, not NP-complete. Exists if P ≠ NP (Ladner).
RelativizationA proof that still works with any oracle. Cannot settle P vs NP.