MathLabs

Applied and computational mathematics

Computational complexity: P and NP

Classifies problems by how the time to solve them grows, centered on whether P equals NP.

IntuitionWhy some puzzles are easy to check but hard to solve

Solving a Sudoku puzzle from scratch can take a long time, trying possibility after possibility. But if a friend hands you a completed grid and claims it solves the puzzle, checking their claim is fast: just scan every row, column, and box for repeats. Sorting a shuffled deck of cards is different — it is fast to solve (a few passes of any sorting method) and just as fast to check. Computational complexity theory makes this everyday distinction — "easy to solve" versus merely "easy to check a solution" — mathematically precise, and asks whether that distinction is real or illusory.

A network of vertices connected by edges, with a subset of edges highlighted forming a single cycle that passes through every vertex exactly once.
A graph where we ask: does a cycle exist that visits every vertex exactly once (a Hamiltonian cycle)? Finding one from scratch seems to require searching through many orderings of vertices, but the highlighted candidate cycle can be checked in a single pass: just confirm every consecutive pair is actually connected by an edge, and every vertex appears exactly once.

SchoolMeasuring running time: polynomial versus exponential growth

An algorithm's running time is usually measured as a function of the input size nn, using big-O notation: T(n)=O(f(n))T(n) = O(f(n)) means the running time grows no faster than a constant multiple of f(n)f(n) once nn is large. Searching an unsorted list of nn items one by one takes O(n)O(n) steps; binary search on a sorted list of nn items takes only O(log⁡n)O(\log n) steps. Both are polynomial (in fact sub-linear or linear) in nn. Trying every subset of nn items, by contrast, takes O(2n)O(2^n) steps — exponential in nn, and vastly slower once nn grows past a few dozen.

How different growth rates scale with input size nn
Growth raten=10n=10n=20n=20n=50n=50
O(n)O(n)101020205050
O(n2)O(n^2)1001004004002,5002{,}500
O(2n)O(2^n)1,0241{,}024≈1.05×106\approx 1.05\times 10^6≈1.13×1015\approx 1.13\times 10^{15}

UndergraduateThe classes P and NP

Definition: The class P

P (polynomial time) is the set of yes/no problems that a standard computer can solve in time bounded by some polynomial in the input size nn — that is, in time O(nk)O(n^k) for some fixed constant kk. Sorting, primality testing, and finding shortest paths in a graph are all in P.

Definition: The class NP

NP (nondeterministic polynomial time) is the set of yes/no problems for which a proposed "yes" answer comes with a certificate (a witness, like the Hamiltonian cycle above or a satisfying assignment to a formula) that can be checked for correctness in polynomial time, even though no polynomial-time way to find such a certificate is known in general. Every problem in P is also in NP (if you can solve it quickly, checking is trivially quick too), so P⊆NPP \subseteq NP; whether the reverse containment holds is exactly the P versus NP question.

P=⋃k≥1TIME(nk)P = \bigcup_{k \ge 1} \mathrm{TIME}(n^k)

Formally, a language LL is in NP if there is a polynomial pp and a polynomial-time verifier VV such that x∈Lx \in L if and only if there exists a certificate yy with ∣y∣≤p(∣x∣)|y| \le p(|x|) and V(x,y)V(x,y) accepts. This verifier-based definition is equivalent to the more common "nondeterministic Turing machine" definition, and is usually easier to reason about in practice.

x∈L  ⟺  ∃ y, ∣y∣≤p(∣x∣), V(x,y)=acceptx \in L \iff \exists\, y,\ |y| \le p(|x|),\ V(x,y) = \text{accept}

UndergraduatePolynomial-time reductions and NP-completeness

Definition: Polynomial-time reduction

Problem AA reduces to problem BB in polynomial time, written A≤pBA \le_p B, if there is a function ff, computable in polynomial time, that transforms any instance xx of AA into an instance f(x)f(x) of BB such that xx is a yes-instance of AA if and only if f(x)f(x) is a yes-instance of BB. Intuitively, A≤pBA \le_p B means "BB is at least as hard as AA": a fast algorithm for BB immediately gives a fast algorithm for AA, by translating and calling it.

Definition: NP-complete

A problem BB is NP-complete if B∈NPB \in NP and every problem A∈NPA \in NP satisfies A≤pBA \le_p B. NP-complete problems are the "hardest" problems in NP: an efficient algorithm for any one of them would translate, via reductions, into an efficient algorithm for every problem in NP.

The Boolean satisfiability problem (SAT) — given a Boolean formula in variables x1,…,xmx_1,\dots,x_m, decide whether some assignment of true/false values makes it evaluate to true — is NP-complete.

Why is it true?

SAT is clearly in NP: a satisfying assignment is a certificate that can be checked in polynomial time by simply plugging in the values. The deep part is showing every NP problem reduces to SAT — this works because a Boolean formula is expressive enough to describe, step by step, the entire run of any polynomial-time verifier, cell by cell and moment by moment, so "does a certificate exist" becomes "is this giant formula satisfiable".

Proof

Let A∈NPA \in NP with polynomial-time verifier VV running in time at most nkn^k on inputs of length nn paired with a certificate of length at most nkn^k. Fix an input xx of length nn; we build a Boolean formula ϕx\phi_x, satisfiable exactly when some certificate makes VV accept.

Imagine the entire computation of VV on (x,y)(x,y), for an unknown certificate yy, laid out as a tableau: an nk×nkn^k \times n^k grid where row tt records the verifier's full tape contents and head position at time step tt. Introduce a Boolean variable for every (cell, time-step, possible symbol) triple, recording what symbol sits in that cell at that time — since the grid has polynomially many cells and time steps, this is a polynomial number of variables.

The formula ϕx\phi_x is built as a conjunction (AND) of clauses enforcing four local, easily-checkable conditions: (1) every cell holds exactly one symbol at each time step; (2) row 00 correctly encodes the fixed input xx followed by a blank placeholder for the yet-to-be-guessed certificate yy; (3) each small window of adjacent cells across two consecutive time steps is consistent with VV's transition rules (this is where the certificate bits, left free in row 00, get to influence the rest of the computation); (4) some cell at the final time step records the accept state.

Each of these conditions only constrains a small, fixed-size neighborhood of variables, so each translates into a constant number of clauses, and there are only polynomially many neighborhoods to constrain — so ϕx\phi_x has polynomial size and is computable from xx in polynomial time. By construction, ϕx\phi_x is satisfiable exactly when there is a consistent tableau, i.e. exactly when some certificate yy makes V(x,y)V(x,y) accept, i.e. exactly when x∈Ax \in A. This exhibits the reduction A≤pSATA \le_p \text{SAT} for an arbitrary A∈NPA \in NP, and combined with SAT∈NP\text{SAT} \in NP shown above, proves SAT is NP-complete.

If BB is NP-complete and B∈PB \in P, then P=NPP = NP.

Why is it true?

NP-completeness of BB means every NP problem can be rewritten, in polynomial time, as an instance of BB. If BB itself can then be solved in polynomial time, chaining the rewriting step and the solving step together solves the original NP problem in polynomial time too — so a single tractable NP-complete problem drags every NP problem down into P with it.

Proof

Suppose BB is NP-complete and there is an algorithm solving BB in time O(nk1)O(n^{k_1}). Take any A∈NPA \in NP; by NP-completeness of BB, A≤pBA \le_p B, meaning there is a reduction function ff computable in time O(nk2)O(n^{k_2}) that maps instances of AA to instances of BB while preserving yes/no answers.

On an input xx of length nn for AA, first compute f(x)f(x): this takes time O(nk2)O(n^{k_2}), and in particular the output f(x)f(x) has length at most O(nk2)O(n^{k_2}) (a polynomial-time algorithm cannot write more output than time it runs). Then run the polynomial-time algorithm for BB on f(x)f(x): since ∣f(x)∣=O(nk2)|f(x)| = O(n^{k_2}), this takes time O((nk2)k1)=O(nk1k2)O\big((n^{k_2})^{k_1}\big) = O(n^{k_1 k_2}).

The total running time is O(nk2)+O(nk1k2)=O(nk1k2)O(n^{k_2}) + O(n^{k_1 k_2}) = O(n^{k_1 k_2}), still a polynomial in nn (the composition of two polynomials is a polynomial). By the correctness of the reduction, xx is a yes-instance of AA if and only if f(x)f(x) is a yes-instance of BB, so this combined procedure correctly decides AA in polynomial time.

Since A∈NPA \in NP was arbitrary, every NP problem has a polynomial-time algorithm, i.e. NP⊆PNP \subseteq P. Combined with the always-true containment P⊆NPP \subseteq NP, this gives P=NPP = NP.

Complexity status of some famous problems
ProblemKnown status
Sorting a listIn P: O(nlog⁡n)O(n\log n) comparisons suffice
Primality testingIn P since 2002 (AKS algorithm)
Boolean satisfiability (3-SAT)NP-complete (Cook–Levin, 1971)
Traveling salesman (decision version)NP-complete
Graph isomorphismIn NP; quasi-polynomial algorithm since 2015 (Babai); not known to be in P or NP-complete
Integer factoringIn NP and co-NP; not known to be in P — hardness assumption behind RSA

UndergraduateReal-World Applications and Worked Examples

Recognizing that a problem is NP-complete has immediate practical value: it tells engineers to stop searching for an exact, always-fast algorithm and instead reach for heuristics, approximation algorithms, or special-case structure. It also underlies modern cryptography (RSA's security rests on factoring being hard), logistics and scheduling (vehicle routing, exam timetabling), bioinformatics (protein structure prediction, sequence alignment variants), and compiler optimization (register allocation is graph coloring, which is NP-complete).

Example: Turning a vertex cover into an independent set

Consider the path graph on 55 vertices {1,2,3,4,5}\{1,2,3,4,5\} with edges (1,2),(2,3),(3,4),(4,5)(1,2), (2,3), (3,4), (4,5). Using the fact that a graph GG with nn vertices has a vertex cover of size kk if and only if it has an independent set of size n−kn-k, and given that {1,3,5}\{1,3,5\} is a maximum independent set of this graph, find the minimum vertex cover size.

Solution

First verify {1,3,5}\{1,3,5\} really is independent: none of the edges (1,2),(2,3),(3,4),(4,5)(1,2), (2,3), (3,4), (4,5) has both endpoints in {1,3,5}\{1,3,5\}, so no two chosen vertices are adjacent, confirming it is a valid independent set of size 33; it is maximum since a 55-vertex path cannot have 44 pairwise non-adjacent vertices (some two would have to be adjacent along the single path).

Apply the reduction formula with n=5n=5 and independent set size 33: minimum vertex cover size =n−3=5−3=2= n - 3 = 5 - 3 = 2.

Verify directly: the complement set {2,4}\{2,4\} should be a vertex cover. Check every edge has at least one endpoint in {2,4}\{2,4\}: edge (1,2)(1,2) touches 22; edge (2,3)(2,3) touches 22; edge (3,4)(3,4) touches 44; edge (4,5)(4,5) touches 44. All four edges are covered by just 22 vertices, confirming the answer.

This equivalence between vertex cover and independent set is exactly the kind of polynomial-time reduction (in fact, here a very simple one, computable in linear time and reversible) discussed above: both Vertex Cover and Independent Set are NP-complete decision problems, and this reduction shows they are, in a precise sense, the same problem viewed from two different angles.

Example: Why brute force fails even at modest size

A brute-force algorithm for a satisfiability problem with nn Boolean variables checks all 2n2^n possible truth assignments, spending about 10−910^{-9} seconds (one nanosecond) per assignment on a fast computer. Estimate, to the nearest power of ten, how many seconds this brute-force search takes for n=50n=50 variables.

Solution

The number of assignments to check is 2502^{50}. Since 210=1024≈1032^{10} = 1024 \approx 10^3, we get 250=(210)5≈(103)5=10152^{50} = (2^{10})^5 \approx (10^3)^5 = 10^{15}; more precisely 250≈1.1259×10152^{50} \approx 1.1259 \times 10^{15}.

Multiplying by the per-assignment cost of 10−910^{-9} seconds gives a total time of roughly 1.1259×1015×10−9=1.1259×1061.1259 \times 10^{15} \times 10^{-9} = 1.1259 \times 10^{6} seconds.

Rounded to the nearest power of ten, this is about 10610^{6} seconds — roughly 1111 to 1313 days of continuous computation, just for n=50n=50 variables, a size that would be considered small in real applications (industrial SAT instances routinely have thousands or millions of variables).

This is exactly why the distinction between polynomial time and exponential time matters in practice, not just in theory: a hypothetical polynomial algorithm running in, say, n3n^3 steps would need only 503=125,00050^3 = 125{,}000 steps — a fraction of a millisecond — showing why the P versus NP question has such enormous practical stakes.

For n=20n=20, which quantity is larger: n3n^3 or 2n2^n?

What does "NP" actually stand for?

According to the Cook–Levin theorem, which problem was the first ever proven to be NP-complete?

If someone discovered a polynomial-time algorithm for a single NP-complete problem such as SAT, what would follow?

References

  1. Stephen A. Cook (1971). The Complexity of Theorem-Proving Procedures · DOI:10.1145/800157.805047
  2. Michael Sipser (2012). Introduction to the Theory of Computation
  3. Michael R. Garey, David S. Johnson (1979). Computers and Intractability: A Guide to the Theory of NP-Completeness
  4. Clay Mathematics Institute (2000). P vs NP Problem