The Class NP

Suppose a colleague hands you a Sudoku solution and asks whether it is correct. Checking it is fast. You scan each row, column, and box in time polynomial in the grid size. But finding that solution from a blank puzzle feels utterly different. It often requires an exhaustive search through an astronomical number of possibilities. This asymmetry between checking and finding is the seed of one of the deepest ideas in theoretical computer science: the complexity class NP. NP formalizes the notion of "efficiently checkable" problems. It contains every decision problem whose yes-instances carry a short proof — a certificate. A polynomial-time algorithm can verify this certificate. Sudoku, graph coloring, the traveling salesman decision problem, integer factorization, and propositional satisfiability all live in NP. A proposed solution can be checked quickly, even though no efficient method for finding one is known. Understanding NP precisely is the entry point to the entire theory of computational hardness. So is understanding why its two standard definitions coincide. NP matters because it captures the shape of an enormous number of real problems. Scheduling, circuit design, planning, cryptographic security, protein folding, and package management all reduce to NP-flavored search-and-verify tasks. The famous P versus NP question asks whether every efficiently verifiable problem is also efficiently solvable. It is arguably the central open problem in mathematics and computer science. A million-dollar Clay Institute prize is attached to it. This article builds the formal definition of NP from two independent angles. It proves they agree, and equips you with the vocabulary needed for NP-Completeness and the The Cook-Levin Theorem. By the end you will be able to state both definitions of NP precisely. You will construct nondeterministic Turing machines and verifiers for concrete problems. You will prove the equivalence of the two formulations. And you will place NP correctly relative to P inside the landscape of complexity classes that includes coNP and the The Polynomial Hierarchy.

Key takeaways
  • NP = languages decided by a nondeterministic Turing machine in polynomial time (guess-and-check in parallel branches).
  • NP = languages with polynomial-time-verifiable certificates (guess-and-check with an explicit proof string).
  • These two definitions are provably equivalent — nondeterministic branching and certificate verification are two views of the same power.
  • P ⊆ NP always; whether P = NP is unknown and is the central open problem of the field.
  • NP contains SAT, Hamiltonian path, subset sum, graph coloring, and thousands of practically important search problems.
  • NP is closed under union, intersection, and concatenation, but closure under complement is the open question behind coNP.
The core insight

NP is exactly the class of problems where a "yes" answer can be confirmed quickly, even when it cannot obviously be found quickly.

The idea

A nondeterministic Turing machine (NTM) generalizes the ordinary (deterministic) Turing machine by allowing its transition function to specify a set of possible next configurations at each step, rather than a single one. Computation is no longer a single thread but a branching tree of possibilities. An NTM accepts an input if at least one branch of this tree reaches an accepting state; it need not be told which branch to take. The class NP (Nondeterministic Polynomial time) consists of all languages decided by some NTM whose longest accepting branch, on any input of length nn, has length bounded by p(n)p(n) for some fixed polynomial pp. Intuitively, an NTM decides a language by being allowed to "guess" a solution and then verify it, where the guess itself costs no time — every guessed branch runs in parallel, free of charge. The second, more operational, definition replaces guessing with an explicit certificate (also called a witness or proof). A language LL is in NP if there exists a deterministic polynomial-time algorithm V(x,c)V(x, c) — the verifier — and a polynomial pp, such that for every string xx:

x∈L  ⟺  ∃ c with ∣c∣≤p(∣x∣) such that V(x,c) accepts.x \in L \iff \exists\, c \text{ with } |c| \le p(|x|) \text{ such that } V(x,c) \text{ accepts.}

Here cc is the certificate: for Sudoku it is the completed grid; for graph 3-colorability it is the coloring; for SAT it is a satisfying assignment. The verifier never has to search for cc — it is handed cc as extra input and only needs to check the claim, in time polynomial in ∣x∣|x| alone. This definition matches ordinary programming intuition far better than nondeterministic branching does, which is why most NP-membership proofs in practice are given as "here is the certificate, here is the polynomial-time checker." Crucially, both definitions demand polynomial-size certificates and polynomial-time checking — dropping either bound trivializes the class (every language would qualify) or makes it far too weak. The word "nondeterministic" in NP is a historical artifact of the first definition; it does not mean "random" or "probabilistic." Nondeterminism here is a mathematical fiction — a bookkeeping device for "try everything at once" — used to define a class of languages, not a physically realizable machine.

How it works

View 1 — Nondeterministic Turing machine. An NTM is described by a transition function

δ:Q×Γ→P(Q×Γ×{L,R}),\delta: Q \times \Gamma \to \mathcal{P}(Q \times \Gamma \times \{L,R\}),

mapping a (state, tape-symbol) pair to a set of possible (state, symbol, direction) triples, instead of a single triple as in the deterministic case. A computation is a tree: the root is the start configuration, and each node has one child per element of δ\delta applicable there. The NTM accepts xx if some root-to-leaf path in this tree ends in the accept state. The running time of the NTM on xx is the length of the longest branch of the tree over all sequences of choices (finite, since we only consider machines that halt on every branch). NP is the set of languages decided this way in time O(nk)O(n^k) for some constant kk.

View 2 — Verifier. A verifier VV is an ordinary deterministic algorithm taking two inputs, the instance xx and a candidate certificate cc, and outputs accept or reject. VV runs in time polynomial in ∣x∣|x| only (the certificate length is itself bounded by that same polynomial, so this is really a bound in terms of ∣x∣|x| alone). LL is the language of xx for which some certificate makes VV accept. The two views are unified by a simple simulation argument in both directions. ``` // Simulating a verifier by a nondeterministic machine function NTM_SimulateVerifier(x): // "Guess" a string c of length at most p(|x|) nondeterministically choose c with |c| <= p(|x|) run V(x, c) // deterministic, polynomial time if V accepts: accept else: reject ``` ``` // Simulating a nondeterministic machine by a verifier function Verifier_SimulateNTM(x, c): // c encodes one path through N's computation tree: // a sequence of choice-indices, one per step, up to p(|x|) steps configuration := start configuration of N on x for i := 1 to p(|x|): options := delta(configuration.state, configuration.symbol) if c[i] is not a valid index into options: reject // malformed certificate configuration := apply(options[c[i]]) if configuration is accepting: accept reject // ran out of steps without accepting ``` The first snippet shows that "certificate + polynomial-time checker" can always be re-expressed as "nondeterministic guess + polynomial-time check on that branch": nondeterministic choice of a string is literally the same operation as guessing a certificate. The second snippet shows the converse: any single accepting branch of an NTM's computation tree is describable by a short string of choice indices — at most p(n)p(n) of them, each needing O(log⁡b)O(\log b) bits where bb is the (constant, machine-dependent) branching factor — so that string is a polynomial-size certificate, and replaying it deterministically is the verifier.

Figure
Two equivalent faces of NP: branching search tree vs. certificate + checker
View 1: Nondeterministic Turing Machine s ✓ one accepting branch is enough longest branch ≤ p(n) steps ⇒ NTM runs in poly time View 2: Certificate + Verifier input x certificate c Verifier V poly-time in |x| acc x ∈ L ⇔ ∃ c, |c| ≤ p(|x|), V(x,c) accepts Equivalence: choice sequence along an accepting branch ⇕ encodes exactly ⇕ a polynomial-size certificate c

Complexity & correctness

Complexity bookkeeping. Both formulations fix the same resource budget, just measured differently.

  • Nondeterministic view: the NTM's longest branch, over all inputs of length nn, is O(nk)O(n^k).
  • Verifier view: certificate length ∣c∣≤p(n)|c| \le p(n) and verifier running time O(nk)O(n^k) (as a function of ∣x∣=n|x|=n; note the verifier reads all of cc too, but ∣c∣|c| is itself polynomial, so total time is still a polynomial in nn).

Theorem (equivalence of the two definitions). A language LL is decided by a polynomial-time NTM if and only if LL has a polynomial-time verifier with polynomial-size certificates. Proof sketch, forward direction (NTM ⟹ verifier). Let NN be an NTM deciding LL in time p(n)p(n), with branching factor bounded by a constant bb (the maximum number of choices δ\delta ever offers). Fix x∈Lx \in L. Some accepting branch of NN's computation tree on xx has length at most p(∣x∣)p(|x|). Encode that branch as a string c∈{1,…,b}p(∣x∣)c \in \{1,\dots,b\}^{p(|x|)}, listing which choice was taken at each step (pad with a fixed symbol if the branch halted early). Then ∣c∣=O(p(∣x∣)log⁡b)|c| = O(p(|x|)\log b), still polynomial in ∣x∣|x|. Define the verifier V(x,c)V(x,c) to replay NN's computation on xx, at step ii following choice cic_i, and to accept exactly when this replay reaches an accept state within p(∣x∣)p(|x|) steps. VV is deterministic (no branching — cc resolves every choice) and runs in time polynomial in ∣x∣|x|, since each of the p(∣x∣)p(|x|) replayed steps takes constant machine-work to simulate. By construction V(x,c)V(x,c) accepts iff cc encodes an accepting branch, and such a cc exists iff x∈Lx \in L (since NN decides LL). ■\blacksquare Proof sketch, reverse direction (verifier ⟹ NTM). Let VV be a verifier for LL running in time q(n)q(n) with certificates of length at most p(n)p(n). Build an NTM NN that, on input xx of length nn: (1) nondeterministically writes a string cc of length at most p(n)p(n) onto a work tape, one symbol chosen per step from the tape alphabet (this is exactly what nondeterministic branching allows — trying every possible symbol at every position, in parallel branches); (2) deterministically simulates V(x,c)V(x,c) and accepts iff VV accepts. Every branch of NN corresponds to one candidate certificate cc; some branch accepts iff some certificate makes VV accept, i.e., iff x∈Lx \in L. The longest branch takes O(p(n))O(p(n)) steps to write cc plus O(q(n))O(q(n)) steps to simulate VV, which is polynomial in nn. So NN decides LL in nondeterministic polynomial time. ■\blacksquare Together these two directions establish the equivalence, which is why textbooks freely use "guess and check" language for both formulations interchangeably — they are formally the same class.

Why P ⊆ NP. Any language decidable in deterministic polynomial time is trivially in NP: use the empty certificate (c=εc = \varepsilon) and let the verifier simply run the deterministic polynomial-time decider on xx, ignoring cc. So every polynomial-time-decidable language is polynomial-time verifiable. This containment is unconditional and easy; the reverse containment NP⊆PNP \subseteq P is precisely the open P vs NP question.

Honest tradeoffs. NP membership proofs are usually much easier to produce than P membership proofs, because you only need to exhibit a certificate format and a fast checker — you never need to explain how to find the certificate. This is both NP's power (it captures an enormous swath of natural search problems) and its limitation as a class: NP says nothing constructive about how to solve the problem, only how to confirm a proposed answer. Also note that NP is a class of decision problems (yes/no answers), not optimization or search problems directly, though decision versions ("is there a tour of length ≤k\le k?") usually capture the difficulty of the corresponding search version via binary search over kk and repeated querying.

Worked example

Take Subset Sum: given a finite set of integers S={s1,…,sn}S = \{s_1,\dots,s_n\} and a target tt, decide whether some subset of SS sums exactly to tt.

SUBSET-SUM={ ⟨S,t⟩∣∃ S′⊆S with ∑s∈S′s=t }\text{SUBSET-SUM} = \{\, \langle S, t\rangle \mid \exists\, S' \subseteq S \text{ with } \textstyle\sum_{s\in S'} s = t \,\}

Concrete instance: S={3,7,11,2,9}S = \{3, 7, 11, 2, 9\}, t=20t = 20.

StepActionDetail
1Certificate proposedc={7,11,2}c = \{7, 11, 2\} (encoded as a bit-vector over SS: 1,0,1,1,01,0,1,1,0 marking which elements are chosen)
2Verifier reads ccChecks ∣c∣≤∣S∣|c| \le |S| bits — trivially satisfied, length is exactly n=5n=5
3Verifier sums selected elements7+11+2=207 + 11 + 2 = 20
4Compare to target20=t20 = t ✓
5Verifier acceptsOutput: accept, so ⟨S,t⟩∈\langle S,t\rangle \in SUBSET-SUM

The verifier's pseudocode is short and unambiguous: ``` function Verify_SubsetSum(S, t, c): // c is a bit-vector of length |S|; c[i] = 1 means s_i is selected if length(c) != length(S): reject total := 0 for i := 1 to length(S): if c[i] == 1: total := total + S[i] if total == t: accept else: reject ``` This runs in O(n)O(n) arithmetic steps — certainly polynomial in the input size (the number of bits needed to write down SS and tt) — and the certificate cc has length exactly nn bits, also polynomial. This confirms SUBSET-SUM ∈\in NP, regardless of the fact that no polynomial-time algorithm is known to construct such a cc from scratch (the best known exact algorithms are exponential in the worst case, though pseudo-polynomial dynamic programming exists when tt is small in magnitude — itself a subtlety about what "efficient" means for numeric problems). Contrast this with the nondeterministic-machine view on the same instance: an NTM would, in parallel branches, try every possible bit-vector of length 5 (there are 25=322^5 = 32 of them), and on the branch corresponding to {7,11,2}\{7,11,2\}, it would compute the sum and accept. The bit-vector 1011010110 is precisely the "choice sequence" from the equivalence proof above — nondeterministic guessing and certificate-supplying are the same act described in two languages.

Common pitfalls & traps

PitfallWhy it's wrongFix
Thinking NP means "not polynomial"NP stands for Nondeterministic Polynomial, not "non-polynomial"Read NP as "nondeterministic-poly," a class that in fact contains P
Believing nondeterminism means randomnessAn NTM does not flip coins; it explores all branches as a mathematical abstractionTreat nondeterminism as "guess-and-verify," never probabilistic behavior
Forgetting the certificate-length boundWithout ∣c∣≤p(n)|c| \le p(n), every language trivially "has a certificate" (just encode the whole answer)Always state the polynomial bound on certificate size explicitly
Conflating decision and search/optimization versions"Find the shortest tour" is not literally a language, so it's not directly "in NP"Phrase the decision variant ("is there a tour ≤k\le k?") before invoking NP membership
Assuming an NP algorithm can be run efficiently as-isSimulating an NTM deterministically by exploring all branches costs time exponential in branch countDistinguish "NP membership" (fast to check) from "solvable in P" (fast to find) — these differ unless P = NP

Pioneers & history

The formal machinery of nondeterministic computation and polynomial-time reducibility crystallized in the early 1970s, though its roots trace to Turing's foundational model of computation decades earlier.

Origins · 1 minStephen Cook and how this was discovered
SC
Stephen Cook
1939–present · Formalized NP-completeness and the SAT problem at the University of Toronto, launching the modern theory of computational complexity

RK
Richard Karp
1935–present · Extended Cook's theorem to twenty-one classical combinatorial problems in 1972, demonstrating NP-completeness was pervasive rather than exotic

History
1936
— Alan Turing defines the Turing machine, the deterministic model underlying all later complexity classes.
1965
— Juris Hartmanis and Richard Stearns formalize time-complexity classes, laying groundwork for measuring "efficient."
1971
— Stephen Cook proves the Cook–Levin theorem, showing SAT is NP-complete and defining NP-completeness itself.
1971–1973
— Leonid Levin independently discovers equivalent results in the Soviet Union, later credited jointly with Cook.
1972
— Richard Karp shows twenty-one further problems are NP-complete, revealing the reach of the phenomenon.

Where this matters

NP is not an abstract curiosity confined to textbooks — its reach extends across nearly every corner of applied computing. Cryptography leans on the presumed hardness of NP problems (and harder ones): the security of RSA and related schemes rests on factoring and discrete-log problems that are easy to verify but believed hard to invert, a direct descendant of the NP intuition (though factoring's exact classification relative to P and NP-completeness is itself subtle). Package managers and dependency resolvers (like those in Linux distributions) solve NP-hard constraint-satisfaction instances daily, using heuristics and SAT-solvers rather than guaranteed-fast exact algorithms. Chip design and formal verification tools translate circuit-equivalence and timing-closure problems into SAT instances — leveraging decades of engineering progress on NP-complete SAT solving even though worst-case exponential blowup is never fully eliminated. Operations research — airline scheduling, vehicle routing, exam timetabling — routinely formulates NP-hard optimization problems and accepts approximate or heuristic solutions in production because exact polynomial algorithms are not known to exist. Machine learning training itself touches NP territory: certain neural-network training and clustering formulations (e.g., exact k-means, certain combinatorial feature-selection problems) are NP-hard, motivating the widespread use of convex relaxations and gradient-based approximations instead of exact solvers. Understanding NP precisely tells engineers when to stop searching for a fast exact algorithm and instead invest in heuristics, approximation guarantees, or domain-specific structure — a judgment call underlying an enormous fraction of real-world software engineering for hard problems.

Ask the tutor: Can you show me why Hamiltonian Path is in NP, step by step, using the certificate definition?

Is every NP problem hard to solve?

No. NP contains all of P as a subset, since any polynomial-time decidable language trivially has a polynomial-time verifier that ignores an empty certificate. NP also contains problems believed to be strictly harder than P, called NP-complete problems, but membership in NP alone says nothing about difficulty — only that a proposed solution can be checked quickly. Whether every NP problem is actually as easy to solve as it is to check is exactly the open P versus NP question. Calling an arbitrary NP problem "hard" without further justification is a common but incorrect shortcut; the correct claim requires showing it is NP-complete or otherwise resistant to known techniques.

Does nondeterminism mean the machine makes random choices?

No, and this is one of the most persistent misconceptions. A nondeterministic Turing machine is a mathematical abstraction that explores every possible sequence of choices simultaneously, as branches of a tree, and accepts if any single branch leads to acceptance. It is not a physical model of a machine that "guesses" using randomness or probability; there is no real hardware implementing true nondeterminism at polynomial cost. The term describes an idealized bookkeeping device used purely to define the class NP cleanly, not a proposal for how to build fast computers. Randomized computation is a separate concept, captured by classes like BPP, unrelated in mechanism to nondeterminism.

How exactly do the two definitions of NP relate to each other?

They describe the same set of languages via a direct simulation in both directions. Given an NTM, any accepting branch on an accepted input can be encoded as a short string recording which choice was made at each step; a deterministic verifier can replay that string against the machine's transition function to confirm acceptance in polynomial time, giving a certificate-based description. Conversely, given a verifier and its polynomial certificate bound, an NTM can nondeterministically write every possible certificate string across parallel branches and then deterministically simulate the verifier on each one. Both constructions preserve polynomial time bounds, so the classes defined by each formulation coincide exactly.

Why is the certificate required to have polynomial length?

Without a length bound, the definition collapses and becomes meaningless: any language, including undecidable ones, could trivially be assigned a "certificate" that simply encodes the full correct answer (such as a complete proof of membership, however long), and a verifier could look it up. Bounding the certificate at p(n)p(n) for a fixed polynomial pp forces the proof of membership to be genuinely succinct relative to the instance size, which is what makes "polynomial-time verifiable" a meaningful, restrictive notion. This length bound, paired with a polynomial-time checker, is precisely what makes NP smaller than the class of all decidable languages and gives the class real mathematical content.

Is factoring integers an NP-complete problem?

Factoring is in NP: given a candidate factor, checking that it divides the target integer is easy (division takes polynomial time in the number of digits), so a certificate exists. However, integer factorization is not known to be NP-complete, and many complexity theorists suspect it is not, partly because it also lies in NP ∩ coNP-like structural classes and admits algorithms (quantum algorithms via Shor's algorithm, though that's tier-4 material) that are subexponential classically and polynomial quantumly. Factoring occupies an intriguing middle ground: believed hard for classical computers, exploited by cryptography, but not proven equivalent in difficulty to SAT or other canonical NP-complete problems.

Where does NP sit relative to P, coNP, and PSPACE?

The chain of known containments is P⊆NP⊆PSPACEP \subseteq NP \subseteq PSPACE, and symmetrically P⊆coNP⊆PSPACEP \subseteq coNP \subseteq PSPACE, where coNP is the class of languages whose complements are in NP. Whether P=NPP = NP, whether NP=coNPNP = coNP, and whether NP=PSPACENP = PSPACE are all open questions, though most researchers conjecture all containments are strict. NP sits at the base of the The Polynomial Hierarchy, an infinite tower of classes built by alternating existential and universal quantifiers over polynomial-time predicates, with NP as its first genuinely nontrivial level above P.

Can an NP problem require exponential time to solve, and how would we know?

It is widely conjectured that some NP problems, specifically the NP-complete ones, require exponential time on deterministic machines in the worst case, but this has never been proven — it remains equivalent to proving P≠NPP \neq NP. What is provable is that if any single NP-complete problem is solved in polynomial time, then every problem in NP is solvable in polynomial time, via polynomial-time reductions. This all-or-nothing structure, established by the Cook–Levin theorem, is why NP-complete problems are simultaneously the most practically important and the most theoretically significant objects in the class.

Before this

Where this leads