The Class P
Suppose you are handed a giant road map. You are asked whether two cities are connected by any route at all. This sounds trivial compared to asking for the shortest route. Yet both problems turn out to share something deep. A computer can answer them quickly. Their running time grows only polynomially as the map grows. The class P captures exactly this notion of "efficiently solvable." It is the single most important dividing line in the theory of computation. Problems inside P — sorting, shortest paths, matching, linear programming, primality testing — are the bread and butter of real software. Routers, compilers, databases, and search engines all lean on polynomial-time algorithms every second of every day. The idea of P matters because it gives a precise meaning to "efficiently computable." This meaning is machine-independent and mathematically precise. Before this formalization, "fast enough" was a matter of engineering taste. After it, computer scientists could prove theorems about which problems are tractable in principle. This holds regardless of whether the algorithm runs on a laptop, a mainframe, or a machine built from vacuum tubes. This is the payoff of the Extended Church-Turing Thesis: reasonable models of computation agree, up to polynomial overhead, on what counts as efficient. This article builds on Measuring Time Complexity. There, big-O notation and worst-case running time were introduced as tools. These tools describe an algorithm's growth rate. Here those tools are turned into a definition of a class of languages. This class is the set of all decision problems solvable within a polynomial time bound. This holds no matter which "reasonable" deterministic machine model is used. We will define P precisely. We will show why the definition is robust to changes in machine model. We will walk through the canonical example PATH (graph reachability) in full algorithmic and complexity detail. We will end with the historical context that led Cobham and Edmonds to propose polynomial time as the right notion of tractability. By the end, you should be able to state the formal definition of P. You should be able to prove membership of a problem in P. Do this by exhibiting a polynomial-time decider. You should be able to explain why encoding choices and machine choices don't change the class. You should also see how P sets the stage for the far more subtle class The Class NP. It also sets the stage for the machinery of Polynomial-Time Reductions used to compare problem difficulty.
- P is the class of decision problems (languages) decidable by a deterministic Turing machine in time for some constant .
- P formalizes "efficiently solvable" — it is robust across reasonable deterministic models (multi-tape, RAM, different alphabets) up to polynomial-factor overhead.
- PATH (does a directed graph have a path from to ?) is a canonical member of P, solvable via breadth-first or depth-first search in time.
- Polynomial time composes: running a polynomial-time algorithm as a subroutine of another polynomial-time algorithm stays polynomial, which is why P is closed under natural operations like union, intersection, and complement.
- P is a worst-case, asymptotic notion — a polynomial-time algorithm can still be slow in practice if the constants or exponent are large.
- P sits at the foundation of complexity theory, motivating the harder open question of whether P equals The Class NP.
P is the mathematically precise, machine-independent class of decision problems solvable in time bounded by a fixed polynomial in the input size.
The idea
A decision problem asks a yes/no question about an input, and is formalized as a language: the set of all strings (encoded inputs) for which the answer is "yes." A Turing machine decides a language if, on every input, halts and accepts exactly when the input is in , and rejects otherwise. This total, always-halting behavior is what separates deciding a language from merely recognizing it. The time complexity of is a function giving the maximum number of steps takes on any input of length (worst case). We say runs in polynomial time if there is a constant such that . The class is then defined as
Two design choices inside this definition deserve attention. First, we measure time as a function of , the length of the encoded input string, not of some "natural size" like the numeric value of an integer — this is why primality testing on an integer is polynomial only if it runs in time polynomial in , the number of bits needed to write down, not in itself. Second, the polynomial bound is a worst-case guarantee: it must hold for every input of a given length, not merely on average or for friendly instances. The reason P deserves to be called the class of tractable problems, rather than an arbitrary bookkeeping definition, is a robustness theorem: switching between reasonable deterministic computational models changes the running time by at most a polynomial factor. A single-tape Turing machine can simulate a multi-tape Turing machine with only a quadratic slowdown; a multi-tape machine can simulate a random-access machine (the abstraction behind real computers) with only polynomial overhead; different encodings of the same mathematical object (adjacency lists vs. adjacency matrices for a graph, say) change input length by at most a polynomial factor. Since polynomials composed with polynomials are still polynomials, none of these choices moves a problem into or out of P. This closure property is exactly what licenses the informal habit of describing algorithms in pseudocode or high-level programming languages, rather than raw Turing-machine transitions, when arguing that a problem is "in P."
How it works
To show a language is in P, you exhibit any deterministic algorithm that decides and runs in polynomial time — you do not need to build a literal Turing machine, thanks to the robustness argument above. The general recipe has three parts: 1. Specify the encoding. Fix how inputs (graphs, numbers, formulas) are represented as strings, and let denote the length of that encoding. 2. Describe the algorithm. Give a step-by-step procedure that always halts and outputs accept/reject correctly. 3. Bound the running time. Show the number of elementary steps is for some fixed , independent of the specific input (only depending on ). The canonical textbook example is PATH:
Here denotes a reasonable encoding of the graph together with distinguished nodes and — for instance, an adjacency list. The algorithm that decides PATH is breadth-first search (BFS), marking every node reachable from and then checking whether got marked. ``` Algorithm DECIDE-PATH(G, s, t): // G is given as an adjacency list over vertex set V, edge set E // n = |V| + |E| (roughly the encoding length) if s == t: return ACCEPT create boolean array visited[1..|V|], all initialized to false create empty queue Q visited[s] = true enqueue(Q, s) while Q is not empty: u = dequeue(Q) for each edge (u, v) in adjacency-list(u): if not visited[v]: visited[v] = true if v == t: return ACCEPT enqueue(Q, v) return REJECT ``` The algorithm never guesses, never backtracks, and never revisits a node twice — every vertex is enqueued at most once, guaranteeing termination. Correctness rests on a simple loop invariant, proved by induction on the number of iterations of the while-loop: > Invariant. At every point in the algorithm, `visited[v] = true` if and only if there is a directed path from to using only vertices that have already been dequeued as intermediate stops, or . Formally, one shows by induction on BFS "layers" (distance from ) that a vertex is marked visited exactly when the shortest directed path from to has been discovered — every vertex reachable from eventually gets enqueued, because if is reachable and visited, every out-neighbor of becomes visited in the same iteration that is dequeued. Since the graph is finite, this process reaches a fixed point where no more nodes can be marked, and at that fixed point the visited set is exactly the set of vertices reachable from . So the algorithm accepts iff is reachable from , iff PATH. The following diagram shows a small instance being processed by BFS layer by layer, alongside the queue state, making the invariant concrete.
The trace box at the bottom of the figure mirrors exactly what the pseudocode's queue does: is dequeued first and pushes ; then is dequeued and pushes ; then is dequeued (its only neighbor is already visited, so nothing new is added); then is dequeued and discovers , causing immediate acceptance. Node is never visited because it is unreachable from along the directed edges shown — the algorithm correctly never marks it.
Complexity & correctness
Time complexity of DECIDE-PATH. Let be the length of the encoding of , so under a standard adjacency-list encoding (every vertex and every edge contributes a constant number of symbols). The algorithm:
- Initializes `visited` in time.
- Enqueues and dequeues each vertex at most once, because the `visited` check prevents duplicate enqueues — this gives queue operations, each .
- For each dequeued vertex , scans its adjacency list, whose total length summed over all is exactly (each directed edge appears once). So the total work done across all scans is .
Adding these up gives total running time — in fact linear in the input size, comfortably a polynomial (take ). This places PATH in P.
Why "polynomial in the model" is not fragile. One might worry: what if we had used a single-tape Turing machine instead of pseudocode with arrays and queues? The standard simulation theorems answer this. A -tape Turing machine running in time can be simulated by a single-tape machine in time ; a random-access machine (arrays with -time indexed access, as used in the pseudocode above) can be simulated by a multi-tape Turing machine with only polynomial overhead (typically or better, depending on the RAM's word-size assumptions). Composing a polynomial with a polynomial is still a polynomial:
So if an algorithm described in RAM pseudocode runs in polynomial time, the Turing machine realizing that algorithm also runs in polynomial time, just with a possibly larger exponent. This is precisely why complexity theorists are permitted to design algorithms in high-level pseudocode (as above) and immediately conclude Turing-machine membership in P, without ever writing transition tables.
Correctness recap. The BFS invariant stated earlier — visited holds iff is reachable from through already-processed vertices — is proved by strong induction on the order in which vertices are dequeued, using the fact that BFS processes vertices in non-decreasing order of distance from (a consequence of the FIFO queue discipline: a vertex at distance is only enqueued after all vertices at distance have been dequeued). Termination is guaranteed because the queue can hold each of the vertices at most once, so the loop runs at most iterations of dequeuing, each doing bounded work. Total correctness (halts + always gives right answer) is what makes DECIDE-PATH a decider, not just a recognizer — crucial for language membership in P, since P is defined via total, always-halting machines.
Honesty about tradeoffs. Polynomial time is a coarse, asymptotic, worst-case notion. An algorithm running in is "efficient" by this definition yet catastrophic in practice; an algorithm running in is "inefficient" by this definition yet perfectly fine for small . P is the right theoretical dividing line for classifying problems by inherent difficulty, but engineers still care about the hidden constants, the actual exponent, and average-case behavior — none of which the definition of P sees.
Worked example
Let's trace DECIDE-PATH on a concrete instance step by step, using the exact graph from the figure: vertices , directed edges , , , , , and isolated (no incoming or outgoing edges relevant to reachability from ). We ask: is PATH?
| Step | Action | `visited` set after step | Queue after step |
|---|---|---|---|
| 0 | Initialize; , mark visited, enqueue | ||
| 1 | Dequeue ; scan out-edges , ; mark and enqueue both | ||
| 2 | Dequeue ; scan out-edge ; mark and enqueue | ||
| 3 | Dequeue ; scan out-edge ; already visited, skip | ||
| 4 | Dequeue ; scan out-edge ; not visited, mark it; since → ACCEPT | (halts) |
The algorithm halts and accepts after 5 constant-time iterations of the outer loop on a graph with and , well within the bound. Vertex was never touched — correctly, since it is not reachable from . If we instead asked whether PATH, the same algorithm would exhaust the queue (after step 4, the queue empties without ever marking ) and correctly REJECT. To see the polynomial bound is not just a coincidence on this tiny example, imagine scaling up: chain together copies of this pattern in sequence (), producing a graph with vertices and edges. BFS still visits each vertex once and scans each edge once, giving running time — linear, hence polynomial, regardless of how large grows. This is the essence of "efficiently solvable independent of instance size": the growth rate of the work, not the work on one fixed instance, is what membership in P certifies.
Common pitfalls & traps
| Pitfall | Why it happens | Fix |
|---|---|---|
| Confusing "polynomial in the input's numeric value" with "polynomial in input length" | Forgetting that an integer is written with bits, so an -time algorithm (like trial division) is actually exponential in input length | Always measure as the length of the encoded string, e.g., for numeric inputs |
| Treating average-case fast algorithms as "in P" without a worst-case bound | Many algorithms (e.g., naive quicksort, simplex method) are fast in practice but have exponential worst-case inputs | P requires a bound that holds on every input of length , not merely typical ones |
| Assuming a clever encoding can shrink a problem out of an exponential class into P | Believing exponential blowups are artifacts of a "bad" representation | Reasonable encodings differ from each other by at most polynomial factors; genuine exponential blowup persists across encodings |
| Forgetting the machine must halt on every input | Recognizers that loop forever on "no" instances are not deciders and don't establish P-membership | Always argue termination explicitly (e.g., via a strictly shrinking resource like the queue size in BFS) |
| Sloppy "clearly polynomial" hand-waving without summing subroutine costs | Nesting a polynomial-time subroutine inside a polynomial number of calls can silently blow up the degree if not tracked carefully | Explicitly multiply/add costs: calling an routine times gives , not |
Pioneers & history
Origins · 1 minAlan Cobham and how this was discovered
Alan Cobham worked at IBM and is credited with the 1965 insight that polynomial time — independent of the specific model of computation — gives a machine-independent notion of "feasible" computation, a step that predates and anticipates the P vs. NP question.
Edmonds independently articulated the idea, in his 1965 paper on matching algorithms, that a "good algorithm" should run in a number of steps bounded by a polynomial in the size of the input, distinguishing genuinely efficient algorithms (like his blossom algorithm for maximum matching) from merely finite but impractical ones.
Where this matters
Every time a GPS app finds any route between two points before optimizing for the shortest one, it is implicitly relying on the fact that reachability — PATH — is decidable in linear time; the same BFS/DFS machinery underlies network routing protocols, dependency resolution in build systems and package managers, and garbage collectors tracing reachable objects in memory. Compilers rely on P-time algorithms for parsing (context-free language recognition via dynamic programming), register allocation heuristics, and data-flow analysis. Databases lean on polynomial-time query evaluation and indexing structures. Cryptographic protocols are built on the presumed hardness of certain problems believed to lie outside P (like integer factorization), making the boundary of P itself a load-bearing wall for internet security. The entire research program of proving polynomial-time algorithms — or proving hardness via Polynomial-Time Reductions to problems believed to be outside P — traces back to the clean, robust definition given here, and the still-unresolved question of whether P equals The Class NP is widely regarded as the most important open problem in theoretical computer science.
Ask the tutor: How does the definition of P change (if at all) when we allow randomized or nondeterministic machines instead of deterministic ones?
Is every polynomial-time algorithm actually fast in practice?
Not necessarily. P is an asymptotic, worst-case classification: it guarantees running time grows no faster than some fixed polynomial as input size increases, but says nothing about the hidden constant factors or the size of . An algorithm with running time is polynomial and hence "in P," yet utterly impractical for any input large enough to matter. Conversely, exponential-time algorithms with tiny inputs or favorable structure can run faster in practice than some polynomial-time competitors. P is the right tool for classifying inherent tractability across problem families as size grows without bound, but production engineering still requires attention to real-world constants, cache behavior, and typical-case performance that the theoretical definition deliberately abstracts away.
Does P include problems solvable in constant time or only genuinely "polynomial-growth" problems?
P includes both. Constant-time algorithms (running in regardless of ) and linear-time, quadratic-time, or any -time algorithms all qualify, since constants and small-degree polynomials are themselves valid polynomials. The definition simply requires some fixed exponent that upper-bounds the growth for all sufficiently large ; it does not require the bound to be tight or the problem to be "hard" in any sense. This is why trivial problems (like checking if a string is empty) and more substantive ones (like matching or linear programming) can coexist comfortably inside P — the class draws no distinction between "trivially easy" and "nontrivially but efficiently solvable."
How does P relate to the Extended Church-Turing Thesis?
The Extended Church-Turing Thesis is the (unprovable, empirically supported) claim that any physically realizable model of computation can be simulated by a probabilistic or deterministic Turing machine with at most polynomial overhead. If this thesis holds, then P as defined via Turing machines coincides with "efficiently solvable on any physically buildable computer," giving the class real-world significance beyond pure mathematics. Quantum computing has stress-tested this thesis: certain problems like integer factorization appear efficiently solvable by quantum computers (via Shor's algorithm) but not by classical ones, suggesting the thesis may need refinement for quantum models even though it remains robust across all classical deterministic and probabilistic models discovered so far.
Why is PATH considered such an important example for teaching P?
PATH is simple enough to analyze completely with an elementary algorithm (breadth-first or depth-first search) yet rich enough to illustrate every subtlety of the definition: a nontrivial encoding (graphs as adjacency lists), a nontrivial correctness argument (the reachability invariant), and a clean linear-time bound. It also foreshadows later material directly: its natural optimization variant, shortest path, remains in P (via BFS or Dijkstra's algorithm), while structurally similar-looking problems like the Hamiltonian path problem (does there exist a path visiting every vertex exactly once?) are believed to require exponential time and serve as flagship NP-complete problems, making PATH a natural stepping stone toward the P versus The Class NP distinction.
Is P closed under union, intersection, and complement?
Yes, and the proof is a direct consequence of polynomial composition. Given deciders for in time and for in time , one can decide or by running both and on the input and combining their accept/reject verdicts with a logical OR or AND, in time , still polynomial. Complementation is even simpler: swap 's accept and reject states, since is a total decider (it halts on every input), and the running time is unchanged. This closure is a genuinely nontrivial structural property that fails for some other complexity classes and relies crucially on P's decider-based (always-halting) definition.
Can a problem be in P even if the only known algorithm for it looks exponential?
Yes — membership in P is a statement about the existence of some polynomial-time algorithm, not about which algorithm is currently known or taught. A famous historical example is linear programming: for years the only widely used method (the simplex algorithm) had exponential worst-case running time on adversarial inputs, even though it performed well in practice. It was only in 1979 that Khachiyan's ellipsoid method proved linear programming is genuinely in P, later followed by more practical polynomial-time interior-point methods. This illustrates that proving a problem lies in P can be a deep mathematical achievement, entirely separate from the practical engineering question of which algorithm to actually deploy.