Tractable vs intractable problems (P vs NP)

You already know from that some algorithms scale gracefully and others fall off a cliff. This page zooms out from a single algorithm to a whole problem and asks a bigger question: is there any reasonably fast algorithm for it at all? For some problems the answer is a confident "yes"; for others, despite decades of the sharpest minds trying, the only methods we know amount to little more than trying everything — and that gets hopeless with terrifying speed.

That split has a name. A problem is tractable if we have an algorithm that solves it in polynomial time; it is intractable if every algorithm we know takes exponential time. In this one page you'll learn exactly where that line falls and why it matters, meet the two famous classes P and NP, see why a Sudoku is fiendish to solve but trivial to check, and meet the most famous unanswered question in all of computer science: does P = NP?

The dividing line: polynomial vs exponential

The dividing line between tractable and intractable is drawn at polynomial time. An algorithm runs in polynomial time if its worst-case step count is bounded by n raised to some fixed power — O(n), O(n^2), O(n^3), even O(n^{100}) — where n is the size of the input. These are the growth classes that keep up as inputs get big.

Why draw the line exactly there? Because polynomials behave themselves and exponentials do not. Double the input to a polynomial algorithm and the work grows by a fixed factor (O(n^2) work goes up fourfold — annoying, but survivable). Add just one to the input of an O(2^n) algorithm and the work doubles. A faster computer barely helps: buy a machine a thousand times faster and you can handle an input just ten items larger, because 2^{10} \approx 1000. That is the practical meaning of "intractable" — not "slow", but uncatchable.

See why exponential is a different beast

The chart plots the number of steps against the input size n for a couple of polynomial curves (n and n^2) against the exponential 2^n. Watch what happens: for tiny n on the left the exponential is actually the lowest line — it looks harmless. But it curls upward and, within a handful of steps, rockets clean off the top of the frame, leaving even n^2 looking flat by comparison.

That crossover is the whole story. The polynomial curves are the ones we can live with; the exponential is the one that dooms us. To feel just how violent the gap becomes, look at the actual step counts — imagine each step takes a billionth of a second (a fast modern computer):

input n n² steps 2ⁿ steps 2ⁿ at a billion steps/sec 10 100 1,024 instant 30 900 1,073,741,824 about 1 second 50 2,500 ~1.13 × 10¹⁵ about 13 days 70 4,900 ~1.18 × 10²¹ about 37,000 years 100 10,000 ~1.27 × 10³⁰ far longer than the age of the universe

The n^2 column stays comically small the whole way down. The 2^n column starts small, then annihilates any hope of finishing. An intractable problem with an input of a hundred items isn't "hard" — it is, for exact answers, permanently out of reach.

P: the problems we can solve quickly

Computer scientists gather all the tractable decision problems into one big club called P — for Polynomial time. A problem is in P if there exists an algorithm that solves it from scratch in polynomial time. If you can name a real algorithm whose worst case is O(n^k) for some fixed power k, the problem is safely inside P.

Loads of everyday problems live here: sorting a list, searching it, finding the shortest path between two towns, checking whether a number is prime, multiplying two matrices. These are the problems we happily throw at a computer and expect an answer back before we've finished our tea. P is, roughly, "the problems computers are actually good at."

NP: the problems we can check quickly

Now here is the subtle, beautiful idea. Forget solving for a moment and think about checking. Suppose someone hands you a proposed answer — a filled-in Sudoku grid, a complete timetable, a suggested route — and simply claims "here, this works." How long does it take you to verify their claim?

NPNondeterministic Polynomial time — is the class of problems whose proposed solutions can be checked in polynomial time, even if finding one from scratch might be brutally hard. The magic word is a certificate: a candidate answer that, if correct, lets you confirm "yes" quickly.

Notice something important: every problem in P is also in NP. If you can solve a problem quickly, you can certainly check an answer quickly — just solve it yourself and compare. So P sits inside NP. The interesting problems are the ones in NP that we don't (yet) know how to fit into P: easy to check, seemingly hard to solve.

NP does not stand for "not polynomial" — a very common and tempting misreading. It stands for nondeterministic polynomial. Picture a magical computer that, whenever it faces a choice, splits and tries all options at once — a "nondeterministic" machine. Such a machine could guess the right certificate and then verify it in polynomial time. Real computers can't branch like that, so for us NP is best understood through its equivalent, tamer definition: the problems whose answers can be checked quickly. Same class, far friendlier picture.

The classic example: hard to solve, easy to check

Sudoku on the ordinary 9 \times 9 grid is a gentle puzzle, but generalise it to an n^2 \times n^2 grid and it becomes a genuine NP problem, and it perfectly captures the asymmetry at the heart of this page:

The little program below makes that concrete. checkSolution verifies a small completed grid in a handful of passes — a polynomial-time check. Contrast that with the effort it would take to discover that grid from a nearly empty one. Press Run.

// A finished 4x4 "mini-Sudoku": each row, column and 2x2 box must hold 1..4 once. const grid = [ [1, 2, 3, 4], [3, 4, 1, 2], [2, 1, 4, 3], [4, 3, 2, 1], ]; function noRepeats(cells: number[]): boolean { return new Set(cells).size === cells.length; // 4 distinct values? } function checkSolution(g: number[][]): boolean { let checks = 0; // rows for (let r = 0; r < 4; r++) { checks++; if (!noRepeats(g[r])) return false; } // columns for (let c = 0; c < 4; c++) { checks++; if (!noRepeats([g[0][c], g[1][c], g[2][c], g[3][c]])) return false; } // 2x2 boxes for (let br = 0; br < 4; br += 2) { for (let bc = 0; bc < 4; bc += 2) { checks++; if (!noRepeats([g[br][bc], g[br][bc + 1], g[br + 1][bc], g[br + 1][bc + 1]])) return false; } } console.log(`Checked the whole grid in just ${checks} passes.`); return true; } console.log("Is this a valid solution?", checkSolution(grid)); console.log("Verifying was quick. FINDING it from a blank grid is the hard part!");

That gulf — trivial to check, punishing to solve — is exactly what puts a problem in NP but leaves us unsure whether it belongs in P. Which brings us to the million-dollar question.

The famous open question: does P = NP?

We know P is a part of NP. The staggering unknown is whether they are actually the same club. Restated in plain words:

Almost everyone believes P \neq NP — that some problems really are fundamentally harder to solve than to check — but believing is not proving. If someone ever showed P = NP with a practical algorithm, the consequences would be earth-shaking: every quick-to-check problem (protein folding, optimal timetables, countless logistics puzzles) would suddenly become quick-to-solve. And, as we'll see, a great deal of the security we rely on every day would collapse.

Inside NP sits a special inner circle of the very hardest problems, called NP-complete — problems (like generalised Sudoku, the travelling salesman decision problem, or Boolean satisfiability) that are, in a precise sense, all "equally hard": a fast algorithm for any one of them could be converted into a fast algorithm for all of NP. That's why they're the battleground for P vs NP. Crack one NP-complete problem in polynomial time and you've proved P = NP in a single stroke; that's also strong evidence that nobody will, since so many brilliant people have tried and failed on so many of them.

Living with intractability: heuristics and approximations

If a problem is intractable, we can't just give up — real businesses still need delivery routes and timetables. So instead of demanding a perfect answer in exponential time, we settle for a good-enough answer in polynomial time. Two everyday tools:

The travelling salesman problem — find the shortest round-trip visiting every city once — is the poster child. Solving it exactly is intractable, so no courier company on Earth does that. They run heuristics and approximations and cheerfully accept a route that's within a whisker of optimal but computed before lunch. Good-enough-and-fast beats perfect-but-never.

Why cryptographers love hard problems

Here's a delicious twist: intractability isn't only a nuisance — sometimes it's exactly what we want. Much of modern cryptography is deliberately built on problems that are easy one way and believed-to-be-hard the other.

The classic case is factoring. Multiplying two big prime numbers together is easy (polynomial). But taking the huge result and recovering the two primes — factoring it back — is believed to be intractable for classical computers. RSA, one of the workhorses of internet security, hides its secret behind exactly this asymmetry: your browser can lock a message with the product, but an eavesdropper would need to factor an enormous number to unlock it, which would take longer than the universe has left. The hardness is the lock. This is also why a proof that P = NP would be a security catastrophe: it would suggest these "one-way" doors might swing open both ways after all.

Do not confuse intractable with uncomputable (or "undecidable") — they are completely different kinds of "hard." An intractable problem can be solved in principle: a correct algorithm exists, it always terminates with the right answer, it just takes an impractically long time (exponential) once the input is sizeable. Given a small enough input, or infinite patience, you'd get there. The difficulty is one of resources, not of possibility.

An undecidable problem is a different universe of impossible: no algorithm can solve it, for any input, ever — no matter how much time you allow. The famous example is the halting problem: there is provably no general program that can look at any other program and always correctly decide whether it will eventually stop or loop forever. That's not "too slow" — it's impossible, and it was proved so by Alan Turing in 1936.

So the ladder of difficulty runs: tractable (solvable and fast) → intractable (solvable but impractically slow) → undecidable (not solvable at all). Intractable sits in the middle. And a final caution: "intractable" means no known polynomial algorithm — because P vs NP is unsolved, we can't yet be certain one doesn't exist.