Chapter 19

Complexity Theory — P vs NP

College
At a glance
Core ideaP is "solvable fast"; NP is "checkable fast" — is P = NP?
Key termNP-complete — the hardest problems in NP, linked by reductions.
You can…See the verify-vs-solve gap on subset-sum first-hand.
Watch outNP means "nondeterministic polynomial," not "not polynomial."
Theory

Chapter 11 introduced Big-O informally; complexity theory makes it rigorous and asks a sharper question than "how fast": which decidable problems have a fast algorithm, and which don't? Define P (polynomial time) as the class of decision problems solvable by an algorithm whose running time is O(nᵏ) for some constant k — considered "efficiently solvable." Define NP (nondeterministic polynomial time) as the class of decision problems whose proposed solutions can be verified in polynomial time, even when no fast way to find one is known.

Every problem in P is also in NP — if you can solve it fast, you can certainly verify a solution fast. Whether the reverse holds — whether P = NP — is the most famous open problem in computer science, one of the seven Clay Millennium Prize problems. A problem is NP-complete if it is in NP and every other NP problem can be transformed into it in polynomial time (a reduction), making NP-complete problems the "hardest in NP." Cook and Levin proved in 1971 that Boolean satisfiability (SAT) is NP-complete; thousands of practical problems — scheduling, packing, routing — have since been shown NP-complete by reduction to SAT or to each other.

Explanation

The gap between "verify" and "solve" is the crux. Given a proposed route for the travelling salesman, checking its total distance is fast — a single pass, O(n). But finding the shortest such route among all possible orderings appears to require checking an exponential number of candidates — nobody has found a fundamentally faster way for the general case, despite fifty years of trying by the best minds in the field. Most computer scientists believe P ≠ NP — that verifying is genuinely easier than solving — but no proof exists either way.

This is not merely academic. Modern cryptography (Chapter 21) depends on certain problems, like factoring large numbers, being hard to solve but the resulting keys easy to verify. If someone proved P = NP and produced a practical fast algorithm, most of today's encryption would become breakable overnight.

Precision matters. "NP" does not stand for "not polynomial" — it stands for "nondeterministic polynomial," referring to verification, and P is a subset of NP, not its opposite. A common beginner mistake is thinking NP means "slow"; it means "efficiently checkable," a very different claim.

Practical

The subset-sum problem — does some subset of a list of numbers add up to exactly a target? — is NP-complete. We show the gap directly: checking a candidate answer is fast (linear), while searching for one by brute force is exponential.

from itertools import combinations
import time

def verify_subset_sum(numbers, target, candidate_subset):    # O(n): the "NP" verifier
    return sum(candidate_subset) == target and all(x in numbers for x in candidate_subset)

def solve_subset_sum_bruteforce(numbers, target):             # O(2^n): the "solver"
    n = len(numbers)
    for size in range(n + 1):                      # try every possible subset size
        for combo in combinations(numbers, size):  # try every subset of that size
            if sum(combo) == target:
                return combo
    return None

numbers = [3, 34, 4, 12, 5, 2]
target = 9

# Verifying a GIVEN answer is instant, regardless of how it was found:
candidate = (4, 5)
print("verify:", verify_subset_sum(numbers, target, candidate))   # True, O(n) work

# Finding an answer from scratch costs exponentially more as n grows:
start = time.time()
found = solve_subset_sum_bruteforce(numbers, target)
print("solved:", found, "in", round(time.time() - start, 6), "s")

# Double the list length and brute force roughly SQUARES the number of subsets checked
# (2^12 vs 2^6 = 64x more work) -- this is what "exponential" costs in practice.

Verification touches each candidate once — cheap and predictable. The brute-force solver enumerates all 2ⁿ subsets in the worst case; no known algorithm avoids this exponential blow-up for subset-sum in general, exactly the signature of an NP-complete problem.

Q&A
Q1 What's the difference between NP-complete and NP-hard?

NP-complete means a problem is in NP and every NP problem reduces to it. NP-hard means every NP problem reduces to it, but it need not itself be in NP — it may not even be verifiable quickly, or may not be a decision problem at all. The halting problem, for instance, is NP-hard but not NP-complete, since it isn't even decidable.

Q2 Why does proving one NP-complete problem is in P solve P vs NP entirely?

Because every NP-complete problem is polynomial-time reducible to every other one, and every NP problem reduces to any NP-complete problem. A fast algorithm for one, composed with these (still polynomial) reductions, yields a fast algorithm for everything in NP — collapsing the two classes together.

Q3 If we can't find fast algorithms for NP-complete problems, what do real systems do?

They use approximation algorithms, heuristics, and problem-specific structure to get "good enough, fast" answers instead of "perfect, slow" ones — real-world route planners, for example, rarely find the mathematically optimal route, just an excellent one, quickly.

Q4 Is the Big-O rigor from Chapter 11 different from complexity theory here?

No — it's the same tool applied more formally. Big-O measures one specific algorithm's growth rate. Complexity theory classifies entire problems by the best possible algorithm for them, which is a deeper, often far harder, mathematical question.

Concept mind map

How the ideas connect

Every key idea in this chapter, branching from the core concept — use it to see the whole picture at a glance.

class Pclass NPverify vs solveNP-completereductionsubset-sumP vs NP
Infographic

The key facts, visualised

P
Problems solvable in polynomial time (solvable fast).
NP
Problems whose solutions can be verified in polynomial time.
NP-complete
The hardest problems in NP; all NP problems reduce to them.
Reduction
Transform one problem into another to compare their difficulty.
Solved examples

Worked problems, step by step

Follow each solution line by line, then try to reproduce it on paper before moving on.

Example 1Subset-sum: does {3, 7, 2} have a subset summing to 9? Verify the guess {7, 2}.

  1. Add the proposed subset: 7 + 2.
  2. Check it equals 9.

Example 2Why is checking a Sudoku solution easy but solving it hard?

  1. Checking scans each row, column, box once.
  2. Solving may explore exponentially many fillings.
Practice problem set

Now you try

Work each one out first, then tap to reveal the worked answer.

1What is the class P?
The set of decision problems solvable by an algorithm in polynomial time.
2What is the class NP?
Problems whose proposed solutions can be checked in polynomial time.
3What is the P vs NP question?
Whether every problem that is fast to verify (NP) is also fast to solve (P); it is unresolved.
4What does NP-complete mean?
A problem in NP to which every NP problem reduces; it is among the hardest in NP.
5Why do reductions matter?
They show that if one NP-complete problem had a fast solver, all NP problems would too.
6Give an example of an NP-complete problem.
Subset-sum, boolean satisfiability (SAT), or the travelling salesman decision problem.