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.
Problems we can solve fast.
Problems whose answers we can check fast.
"A is no harder than B." The main tool.
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).
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?
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."
| Year | Event |
|---|---|
| 1956 | Gödel's letter to von Neumann asks the question |
| 1965 | Edmonds and Cobham: "efficient = polynomial" |
| 1971 | Cook: SAT is NP-complete |
| 1972 | Karp: 21 NP-complete problems |
| 1973 | Levin, independently, in the USSR |
| 2000 | Clay Millennium Prize problem |
Definition. A decision problem is a language L ⊆ {0,1}*. An instance x is a yes-instance iff x ∈ L.
| Search version | Decision version |
|---|---|
| Find a satisfying assignment | Is φ satisfiable? |
| Find the shortest tour | Is there a tour of length ≤ k? |
| Find the largest clique | Is there a clique of size ≥ k? |
| Find a 3-coloring | Is 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.
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.
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.
Caveat: n100 is in P but useless. 1.0001n is not in P but fine for small n.
| Problem | Algorithm |
|---|---|
| Shortest path | Dijkstra, O(m + n log n) |
| Max flow / bipartite matching | Edmonds–Karp, Hopcroft–Karp |
| 2-SAT | Implication graph + SCC, linear |
| Linear programming | Ellipsoid (1979), interior point |
| PRIMES | AKS (2002) |
| 2-coloring | BFS |
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.
Time to run f(n) steps on a machine doing 109 steps per second:
| n | n² | n³ | 2n | n! |
|---|---|---|---|---|
| 10 | 100 ns | 1 µs | 1 µs | 3.6 ms |
| 20 | 400 ns | 8 µs | 1 ms | 77 years |
| 30 | 900 ns | 27 µs | 1.1 s | 8 × 1015 years |
| 50 | 2.5 µs | 125 µs | 13 days | 1048 years |
| 100 | 10 µs | 1 ms | 4 × 1013 years | 3 × 10141 years |
Hardware helps polynomial algorithms a lot. It barely helps exponential ones.
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.
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).
NP stands for Nondeterministic Polynomial time, not "non-polynomial".
| Problem | Certificate | Check |
|---|---|---|
| SAT | assignment | evaluate every clause |
| CLIQUE(k) | k vertices | all pairs adjacent |
| HAM-CYCLE | vertex order | each step is an edge |
| SUBSET-SUM | subset | add it up |
| 3-COLOR | coloring | every edge bichromatic |
| COMPOSITE | a factor | one 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?"
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.
Definition. NP = ⋃k NTIME(nk). A nondeterministic TM accepts x if some branch of its computation tree accepts within O(nk) steps.
Theorem. Verifier-NP = NTM-NP.
(⇒) 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.
Try every branch: NTIME(t) ⊆ DTIME(2O(t)). So NP ⊆ EXP. Nobody knows how to do fundamentally better.
Definition (Karp / many-one). A ≤p B iff there is a poly-time computable f with
x ∈ A ⇔ f(x) ∈ B for every x.
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.
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: 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.
| Problem | In NP? | Status |
|---|---|---|
| SAT, 3SAT, CLIQUE, TSP-decision | yes | NP-complete |
| TSP optimization (output the tour) | not a language | NP-hard |
| Halting problem | no (undecidable) | NP-hard |
| Generalized chess on n×n | no (EXP-complete) | NP-hard |
| 2-SAT, 2-COLOR | yes | in P |
| Graph isomorphism, factoring | yes | unknown, 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.
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.
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.
φx = φcell ∧ φstart ∧ φmove ∧ φaccept
| Part | Says | Size |
|---|---|---|
| φcell | each cell holds exactly one symbol | O(T²) |
| φstart | row 0 is the start config on x | O(T) |
| φmove | every 2×3 window is legal for δ | O(T²) |
| φaccept | qaccept appears somewhere | O(T²) |
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. □
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 size | Replace with |
|---|---|
| 1: (a) | (a∨y∨z)(a∨y∨¬z)(a∨¬y∨z)(a∨¬y∨¬z) |
| 2: (a∨b) | (a∨b∨y)(a∨b∨¬y) |
| 3 | keep it |
| k > 3 | chain with k−3 new variables (right) |
(ℓ1∨ℓ2∨…∨ℓk) ↦
(ℓ1∨ℓ2∨y1)(¬y1∨ℓ3∨y2)…(¬yk−3∨ℓk−1∨ℓk)
⇒ 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.
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.
Map: for a 3-CNF φ with m clauses, build graph G:
⇒ 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.
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.
A set S with no edges inside it. Is there one with |S| ≥ k?
A set S with all edges inside it. Is there one with |S| ≥ k?
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. □
These maps are trivial, but hardness flows through them. One NP-complete member makes all three NP-complete.
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.
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.
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.
"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.
| Reduction | Gadget idea |
|---|---|
| SAT → 3SAT | split long clauses with new variables |
| 3SAT → IS | triangle per clause, conflict edges |
| IS → CLIQUE | complement the graph |
| IS → VC | take V − S |
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.)
Planar 3-coloring is still NP-complete. 4-coloring a planar graph is always possible (4-color theorem).
HAM-CYCLE → TSP: weight 1 on edges, 2 on non-edges. Ask for a tour of cost ≤ n.
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.
φ = C1 ∧ C2, with C1 = (x1 ∨ ¬x2 ∨ x3) and C2 = (¬x1 ∨ x2 ∨ x3).
| number | x1 | x2 | x3 | C1 | C2 | meaning |
|---|---|---|---|---|---|---|
| v1 | 1 | 0 | 0 | 1 | 0 | x1 true |
| v1' | 1 | 0 | 0 | 0 | 1 | x1 false |
| v2 | 0 | 1 | 0 | 0 | 1 | x2 true |
| v2' | 0 | 1 | 0 | 1 | 0 | x2 false |
| v3 | 0 | 0 | 1 | 1 | 1 | x3 true |
| v3' | 0 | 0 | 1 | 0 | 0 | x3 false |
| s1, s1' | 0 | 0 | 0 | 1, 2 | 0 | slack for C1 |
| s2, s2' | 0 | 0 | 0 | 0 | 1, 2 | slack for C2 |
| target | 1 | 1 | 1 | 4 | 4 | = 11144 |
Row vi has a 1 in clause column j iff literal xi is in Cj. Row vi' does the same for ¬xi.
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
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.
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
Cross-checked: brute force and DPLL agree on 300 random formulas, and every DPLL answer passes verify_sat.
MiniSat, Glucose, CaDiCaL, Kissat. They routinely solve industrial formulas with millions of variables.
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).
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.
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.
| Kind | Example | Fast if numbers are small? |
|---|---|---|
| weakly NP-complete | SUBSET-SUM, KNAPSACK, PARTITION | yes (DP) |
| strongly NP-complete | 3-PARTITION, bin packing, TSP | no, 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.
Try each one before you open the answer.
Someone proves 3SAT ≤p 2SAT. What follows?
P = NP. 2SAT is in P. So 3SAT is in P. 3SAT is NP-complete, so all of NP is in P.
Is "is there an s–t path with at most k edges?" NP-complete? What about "at least k edges, no repeats"?
The first is in P (BFS). The second is NP-complete: with k = n−1 it is Hamiltonian path. One word flips the difficulty.
Knapsack has an O(nW) DP. Does that put an NP-complete problem in P?
No. W takes only log W bits to write. So nW = n · 2bits is exponential in the input size. It is pseudo-polynomial.
You reduce your new problem X to SAT. What have you shown?
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.
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.
| Known | Open |
|---|---|
| P ⊆ NP ⊆ PSPACE ⊆ EXP | P vs NP |
| P ≠ EXP | NP 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.
"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."
Impagliazzo's "five worlds" (1995): Algorithmica, Heuristica, Pessiland, Minicrypt and Cryptomania. They describe what the world looks like under each answer.
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.
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. □
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.
Decision rule. Small n: go exact. Small parameter: FPT. Need a guarantee: approximate. Huge and messy: heuristics or a solver.
Theorem (Ladner 1975). If P ≠ NP, then some language in NP is neither in P nor NP-complete.
Define L = { x ∈ SAT : H(|x|) is even }. Here H is a slow-growing function built by diagonalization:
So L is never in P and never NP-complete. Stages last long enough for each witness to be found in poly time. □
| Problem | Best known |
|---|---|
| Integer factoring | GNFS, exp(O(n1/3(log n)2/3)) |
| Graph isomorphism | Babai 2015: quasi-poly 2O(log n)c |
| Discrete log | subexponential |
| Min circuit size (MCSP) | unknown |
Ladner's language is artificial. Whether any natural problem is NP-intermediate is itself open.
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.
Three theorems show that whole families of proof methods cannot settle P vs NP.
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.
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.
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.
Is every NP problem decidable? How fast, at worst?
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.
Show: if P = NP, then NP = co-NP.
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.
TAUT = formulas true under every assignment. Is TAUT in NP?
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.
RSA relies on factoring being hard. Is factoring NP-complete?
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).
No. NP means nondeterministic polynomial. Every P problem is in NP.
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.
You need both "yes gives yes" and "yes back gives yes". Test your reduction with brute force on small cases.
NP-hard plus a verifier gives NP-complete. Without the verifier you only have NP-hard.
Input size is bits. An O(nT) DP is pseudo-polynomial, not polynomial.
Worst case is not your case. SAT and ILP solvers crush many real instances. Approximation and FPT often work.
A claim needs a proof for every input. Hundreds of claimed proofs have failed (see Woeginger's list).
Optimization versions are NP-hard, not NP-complete, because they are not languages.
| Concept | One line |
|---|---|
| P | solvable in poly time |
| NP | yes-answers have poly-checkable certificates |
| co-NP | no-answers have poly-checkable certificates |
| ≤p | poly map keeping yes/no: "A no harder than B" |
| NP-hard | all of NP reduces to it |
| NP-complete | NP-hard and in NP |
| Cook–Levin | SAT is NP-complete (tableau + locality) |
| Ladner | P ≠ NP ⇒ NP-intermediate problems exist |
| Barriers | relativization, natural proofs, algebrization |
| Term | Meaning |
|---|---|
| Decision problem | A question with a yes/no answer, seen as a language. |
| P | Decidable in polynomial time by a normal (deterministic) machine. |
| NP | Yes-answers have short certificates that can be checked in poly time. |
| co-NP | No-answers have short, checkable certificates. |
| Certificate / witness | The short proof a verifier checks (an assignment, a tour, a set). |
| Verifier | A poly-time program V(x, c) that checks a certificate. |
| A ≤p B | A poly-time map turning A instances into B instances, keeping yes/no. |
| NP-hard | Every NP problem reduces to it. Need not be in NP. |
| NP-complete | NP-hard and in NP. The hardest problems in NP. |
| Term | Meaning |
|---|---|
| SAT / 3SAT | Is a CNF formula satisfiable? 3SAT: each clause has 3 literals. |
| Gadget | A small piece of the output instance that encodes one choice or check. |
| Pseudo-polynomial | Polynomial in the value of numbers, not in their bits. |
| Strongly NP-hard | Still hard when numbers are small (written in unary). |
| Approximation ratio | Worst-case ALG / OPT (or OPT / ALG for maximizing). |
| PCP theorem | NP proofs can be checked by reading a few random bits. |
| FPT | Time f(k) · poly(n): exponential only in a parameter k. |
| NP-intermediate | In NP, not in P, not NP-complete. Exists if P ≠ NP (Ladner). |
| Relativization | A proof that still works with any oracle. Cannot settle P vs NP. |