Proof Techniques
An algorithm without a proof is a conjecture. This lesson collects the handful of arguments that certify the algorithms in this course — direct proof, contrapositive, contradiction, ordinary and strong induction, construction, and disproof by counterexample — each with a small worked example and a picture.
╌╌╌╌
Every algorithm in this course comes with four
deliverables, and the third —
proof of correctness — is the one students skip and regret. An algorithm you
cannot argue for is a conjecture: it might be right, but you have no way to know,
and it passed my tests
is not a proof, since one untested instance can sink it.
The arguments you need are few. A small kit of techniques proves nearly everything in algorithms, and the same shapes recur from sorting to intractability. This lesson is that kit. None of it is deep; the skill is recognizing which tool fits a given claim, and writing the argument cleanly enough that a skeptical reader is forced to agree.
Direct proof
The default. To prove if then ,
assume and walk a chain of valid
steps to . Most everyday claims yield to it.
The whole move is to unfold the definitions (odd means ), do the algebra, then fold the definition back (an expression of the form is odd). Many direct proofs follow this shape: translate the hypothesis into symbols, manipulate, translate back.
Proof by contrapositive
The statements if then
and if not then not
are logically
equivalent:
So when the forward direction is awkward, prove the contrapositive instead — it is
the same theorem in an easier form. We used exactly this to prove a search
routine sound in the previous
lesson: rather than reason about the
whole array (if it returns
), we reasoned about one line
of code (not foundif it returns
).found then
Proving even even directly is clumsy (you would factor ); the contrapositive reduces it to a one-line consequence of work already done.
In algorithm design, contrapositives are most useful when they turn a claim about absence into a claim about presence. Here is the argument that lets a trial-division primality test stop at instead of scanning all the way to :
The forward statement quantifies over every candidate divisor and asserts a negative, which gives you nothing to compute with. The contrapositive supplies a concrete object (, the smaller factor) and one inequality. The algorithmic payoff is real: testing a twelve-digit number now takes about trial divisions instead of .
Proof by contradiction
To prove , assume and derive an impossibility. Since valid reasoning from a true premise cannot reach a falsehood, the premise must have been false, so holds. It is the proof of last resort, and often the shortest.
The classic is the irrationality of , the proof the Pythagoreans reputedly found unsettling.
Contradiction also proves things do not exist or cannot be improved. The
lower bound for
comparison sorting and the proof that no greedy coin
system is always
optimal are both suppose a better object existed, derive absurdity
arguments.
Here is the smallest specimen of that genre, a genuine lower bound proved in four
sentences:
The move deserves a name, because it recurs: the contradiction is manufactured by
an adversary who watches what the algorithm does and then plants the bad case
precisely in the spot it never looked. Every you cannot do better than
claim in this course, from sorting to
searching, has this shape: assume a faster algorithm exists, then exhibit an
input it must get wrong.
Induction
Induction is the main tool for proving algorithm correctness, because algorithms repeat: loops and recursion both do the same thing on ever-smaller (or ever-larger) instances.1 To prove a statement for all , you show two things:
- Base case. holds outright.
- Inductive step. Assuming (the induction hypothesis), prove .
Together these are a falling chain of dominoes: the base case tips the first, the step guarantees each tips the next, so all of them fall.
The textbook example is the sum of the first integers, the same closed form the analysis lessons lean on.
Equalities are the gentlest case, because the inductive step is forced: substitute the hypothesis, simplify, done. Inequalities require slightly more judgment: after substituting you must still bridge from what the hypothesis gives you to what the claim demands. Here is the inequality that explains why halving reaches in logarithmically many steps, the fact behind binary search and every divide-and-conquer depth bound:
Read the step closely: the hypothesis delivered , the claim wanted , and the bridge was the observation that the surplus is nonnegative. Most failed induction attempts on inequalities die exactly at that bridge, and the usual repair is to prove a stronger claim whose surplus is bigger.
The art is choosing the hypothesis. Too weak and the step cannot go through; the fix is often to prove a stronger statement, whose hypothesis gives the step more to work with (this is strengthening the induction hypothesis, and it powers the substitution method for recurrences).
Strong induction
Sometimes needs more than its immediate predecessor — it needs several smaller cases, or one you cannot predict in advance. Strong induction grants the entire history: to prove , assume for all .
It is the natural argument whenever a problem splits into smaller subproblems of sizes you do not know ahead of time — which is to say, most of divide & conquer and dynamic programming.
Ordinary induction would be stuck here: 's factorization has nothing to do with 's. Knowing that tells you nothing useful about , and knowing about tells you nothing about . We needed the freedom to reach back to and , wherever they landed. For that means and , neither of which is .
Strong induction proves recursion correct
The payoff of strong induction is that it certifies recursive algorithms almost mechanically: assume every recursive call returns the right answer (each call is on a smaller instance, so the strong hypothesis covers it), then check that the combining logic is sound.1 Here is the standard fast exponentiation routine, which computes in multiplications by squaring:
- 1if then
- 2return
- 3recurse on the half
- 4if is even then
- 5return
- 6else
- 7return
Notice which induction was required. The call from input is on
, not on : for the proof for leans on
the case , skipping the forty-nine cases between. Weak induction's hypothesis
covers only the immediate predecessor and cannot reach that far back. Every
recursive correctness proof in this course follows this template: base case =
the non-recursive branch, hypothesis = the recursive calls are correct,
step =
the combine logic preserves correctness.
The only obligation that varies is
checking the combine.
Structural induction: trees
Induction is not confined to : any collection of objects built
from smaller objects of the same kind supports it. Trees are the canonical case:
a binary tree is either a single leaf or a root with two smaller binary trees
hanging off it, so a claim about all binary trees can be proved by inducting on
the tree's size, with the subtrees playing the role of smaller cases.
This is
structural induction, and it is strong induction in disguise: a root's
subtrees can be any smaller sizes, so the step needs the full history, exactly
as prime factorization did.
The proof never mentions the tree's shape: balanced, degenerate, or lopsided, the count comes out the same, which is why the figure shows a deliberately skewed tree. Facts of this kind recur throughout the course: this one bounds the size of merge trees and decision trees in the sorting lower bound, and its siblings (a binary tree of height has at most leaves) are proved by the same template.2
Where inductions go wrong
Induction fails in stereotyped ways, and each failure mode has a classic specimen. Knowing them keeps your own proofs honest.
A valid step cannot rescue a false base. Consider the claim
for all .
The inductive step is airtight: if
, then
since for . Yet the claim is false: at we would need
, and at we would need . The first domino never falls.
The statement only becomes true at (), so the honest theorem is
for all ,
with base case . Checking where the base actually
starts is not pedantry: an algorithm proved correct for all
still
needs separate handling for and , and a recursive implementation hits
those inputs last and crashes on them first.
Count how far back the step reaches. A step that uses and together (the shape of every Fibonacci-flavored claim) needs two base cases, because the step for reaches down to , which one base case at never established. In general, a step that reaches back positions needs consecutive base cases, and a strong-induction step that recurses to needs the base to cover everything its smallest instances bottom out on. Erickson's advice is to write the step first, see which smaller cases it consumed, and only then decide what the base must be.1
Flawed maintenance: all horses are the same color. The most famous broken
induction proves
that any horses are identically colored. Base case
(): one horse has one color. ✓ Inductive step: given horses,
remove the first; the remaining are same-colored by hypothesis. Remove the
last instead; the first are also same-colored. The two groups overlap, so
all share one color. ?
The flaw is quantifier-shaped: the overlap argument silently assumes the two
groups share at least one horse, which requires . At the very first
application of the step (from to ) the groups are and
with empty intersection, and nothing links their colors. One broken link,
at exactly one value of , and every later domino stands: the proof is valid
for any horses of which some two share a color,
a theorem nobody needs.
The lesson is to test the inductive step at its smallest instance, where
degenerate geometry (empty overlaps, single elements, zero-length ranges) lives.
Assuming what you are proving. The inductive hypothesis is ; the
obligation is . Writing assume
and simplifying it into a true
statement proves nothing, because false premises can yield true conclusions:
from , multiplying both sides by gives the perfectly true .
This mistake usually appears disguised as start with the equation for and manipulate both sides until they match.
The repair is
directional discipline: start from the left side of the claim (or from
the hypothesis), and transform it by known-valid steps until the right side
appears, never touching the target equation as if it were already true.
Proof by construction
To prove something exists, the most convincing move is to build it — exhibit the object and check it works. A constructive existence proof produces a witness, not merely a guarantee.
This is the proof style closest to our subject, because an algorithm is a constructive proof: a correct algorithm for a problem is a witness that a solution exists and a recipe for producing it. When the greedy method proves its choice is safe by an exchange argument — take any optimal solution, transform it into the greedy one without making it worse — that, too, is construction: it builds the optimum it claims exists.
Disproof by counterexample
The arguments above confirm universal claims (for all …
). To refute one,
you need just a single instance where it fails. One counterexample is fatal; no
amount of confirming examples can rescue a false for all.
This is why correctness must hold on every input, and why a plausible heuristic needs a proof rather than a few passing trials. Hunting for a small counterexample is also the fastest way to test a conjecture before investing in a proof3 — a conjecture that survives honest attempts to break it is worth the cost of a proof.
Loop invariants are induction
The reason induction matters so much here is that loop invariants are induction applied to time. A loop invariant is a statement true before and after every iteration; proving it amounts to an induction over the iteration count, and its three-part rubric4 maps one-to-one onto the parts of an inductive proof:
| Loop invariant | Induction |
|---|---|
| Initialization — holds before the first iteration | Base case |
| Maintenance — if it holds before a pass, it holds after | Inductive step |
| Termination — what the invariant gives once the loop stops | Conclusion |
We proved Find-Max correct exactly
this way, and the pattern returns for every loop in the course. To see the rubric
run end-to-end once more, here is the simplest loop that computes anything:
- 1
- 2for to do
- 3fold in the element under the cursor
- 4return
- Initialization. Before the first iteration, and the invariant reads (sum of no elements) . The seed line set , and the empty sum is by convention. ✓
- Maintenance. Assume the invariant entering the iteration with cursor : . The body executes , after which — the invariant with cursor value , the state in which the next iteration begins. ✓
- Termination. The loop exits when the cursor passes , i.e. with the invariant holding for : . That is the specified output, and the last line returns it. ✓
Each bullet is short because the invariant was chosen well: it names the one
quantity the loop maintains, at one fixed instant (the start of an iteration),
and it mentions the cursor so that termination instantiates it at a known value.
A vaguer statement ( holds a partial sum
) passes initialization and
maintenance trivially and then gives you nothing at termination. The rubric only
pays off if the invariant is strong enough that its instance is the
correctness claim.4
Recursion is even more direct: a recursive algorithm is correct by (usually strong) induction on the size of its input, with the base case the non-recursive branch and the inductive step the assumption that the recursive calls are themselves correct, exactly the proof above.
A note on yes/no algorithms
For procedures that decide rather than compute — does a path exist, is this formula satisfiable, is in the array — correctness has two halves with standard names, introduced earlier: soundness (every yes is true — no false positives) and completeness (every true case is caught — no false negatives). The two are proved separately and often with different tools: soundness frequently falls to a direct or contrapositive argument about a single accepting step, while completeness usually wants induction over the algorithm's progress. Keeping them apart keeps the proof honest.
Machine-checked proof, and its limits
Every technique here is a paper-and-pencil proof, checked by a human reader.
Two developments push past that. The first is machine-checked proof: an
a proof assistant like Coq, Lean, or Isabelle can check an inductive argument
mechanically, invariant by invariant, with no gap left to
clearly.
The four-color theorem (1976) was the first famous result whose proof
had to be completed by computer, and modern formalizations — Gonthier's fully
machine-checked four-color and Feit–Thompson proofs — show the same induction and
case analysis we do by hand, scaled to sizes no referee could audit.5
When a correctness proof matters enough that a subtle error would be costly, this
is where the discipline of state the invariant precisely
pays off.
The second is a caution about what a proof of correctness does and does not buy. A verified algorithm is correct relative to its specification; if the specification is wrong, the proof certifies the wrong thing. And correctness is separate from feasibility: proving that a procedure eventually halts with the right answer says nothing about whether it halts before the sun burns out — the running-time analysis of the next lessons is a distinct obligation. Skiena's running advice, to attack a conjecture with small counterexamples before attempting a proof, is the cheap filter that saves the expensive one: most false conjectures die to an instance with three or four elements.6
Takeaways
- Match the tool to the claim.
If then
→ direct, or contrapositive if the reverse is easier. Afor all
about a repeated process → induction (strong induction when a step needs many smaller cases).There exists
→ construction.This always works
that you doubt → hunt a counterexample. - Induction is the spine of correctness proofs, because loops and recursion repeat. Loop invariants are induction over iterations; recursive correctness is induction over input size.
- Strengthen the hypothesis when an inductive step won't close — proving a stronger statement can be easier, because the step gets more to work with.
- Audit the base and the smallest step. A valid inductive step cannot rescue
a false base case, a step that reaches back positions needs base cases,
and flawed maintenance hides at the smallest instance (the horses
proof
breaks only at ). - One counterexample refutes a universal claim; no finite pile of examples
proves one. This is why
it passed the tests
is evidence, not proof. - An algorithm is a constructive existence proof — which is why getting the proof right and getting the algorithm right are the same task, not two.
Footnotes
- Erickson, Algorithms, Appendix on Induction — the
boilerplate
inductive proof and the discipline of an explicit, strong induction hypothesis. ↩ ↩2 ↩3 - CLRS, Appendix B.5 — trees and their basic counting properties; the decision-tree argument that uses them appears in Ch. 8 (§8.1). ↩
- Skiena, The Algorithm Design Manual, §1.3 — reasoning about correctness, and counterexamples as the first line of attack on a conjecture. ↩
- CLRS, Ch. 2 (§2.1) — loop invariants and the initialization / maintenance / termination rubric. ↩ ↩2
- Appel, K. & Haken, W. (1977).
Every planar map is four colorable.
Illinois J. Mathematics 21 — the first computer-assisted proof; Gonthier, G. (2008).Formal proof — the four-color theorem.
Notices of the AMS 55(11), and the machine-checked Feit–Thompson odd-order theorem (2012). ↩ - Skiena, The Algorithm Design Manual, §1.3 — hunting small counterexamples as the first, cheapest test of a conjectured algorithm or claim. ↩
╌╌ END ╌╌