Termination is a separate proof
Find a quantity that strictly decreases at every call and cannot decrease forever. Usually the input size; sometimes something less obvious.
A recursive algorithm mirrors a recursive definition, and its correctness proof mirrors an induction. Two obligations: it terminates, and it returns the right answer when it does.
Core ideas
Correctness of a recursive algorithm splits cleanly in two, and the split is what makes the proofs short.
Find a quantity that strictly decreases at every call and cannot decrease forever. Usually the input size; sometimes something less obvious.
That is the inductive hypothesis. You do not trace the recursion; you assume smaller inputs are handled and check the combination.
And a terminating one that returns nonsense is not either. The two obligations are independent.
The material
Three algorithms, each with both obligations discharged.
Termination. Each call has argument one smaller, and the argument is a natural number, so it cannot decrease past 0. The sequence of calls is finite.
Correctness. By induction on n. At n = 0 the algorithm returns 1, which is 0!. For n ≥ 1, assume the call FACTORIAL(n−1) returns (n−1)!; the algorithm returns n · (n−1)! = n! by the recursive definition of factorial.
Termination. The quantity hi − lo + 1 — the size of the range — strictly decreases at every recursive call, because mid is inside the range and is excluded from both subproblems. It is bounded below by 0, at which point the first line returns.
Correctness. By strong induction on the range size. If the range is empty, x is not in it and NOT FOUND is right. Otherwise, if A[mid] = x the index is right. If A[mid] < x then, because A is sorted, every index at or below mid holds a value less than x, so x can only be in the upper half; the hypothesis says the recursive call handles that smaller range correctly. The other branch is symmetric.
The sortedness assumption is doing the real work: it is what licenses discarding half the array. Applied to unsorted data the algorithm still terminates and returns confident wrong answers, which is the most dangerous kind of failure.
Termination. Each call receives an array of size at most ⌈n/2⌉ < n for n ≥ 2, and size is a natural number.
Correctness. Strong induction on |A|. Arrays of size 0 or 1 are already sorted. Otherwise the hypothesis says both recursive calls return sorted arrays, and MERGE of two sorted arrays is sorted — which is itself a small proof, by induction on the number of elements merged.
Notice the shape. Nothing traced the recursion; the argument assumed the subcalls worked and checked the combining step. That is why recursive correctness proofs are short while tracing recursive execution by hand is not.
An algorithm can terminate and be wrong (binary search on unsorted data), and can be "correct if it terminates" and never terminate (a recursion that fails to decrease). Both proofs are required.
Interactive
Counts from running, not from quoting
Method
Termination first, then correctness assuming the calls.
Input size, range width, remaining depth. It must strictly decrease at every recursive call and be bounded below.
And check that the recursion actually reaches it. A base case for n = 0 with a recursion that skips from 2 to 0 needs care about odd inputs.
This is the strong inductive hypothesis. Do not trace — assume.
Given correct answers on the subproblems, does the algorithm produce the right answer for the whole? That is where the real content is.
Computing aⁿ in about log₂ n multiplications rather than n.
Strong induction is essential here: the even branch reaches back to n/2, not to n − 1. Ordinary induction would give a hypothesis about the wrong value. “Modular Exponentiation” in Number Theory and Cryptography uses this algorithm as modular exponentiation, where the saving is the difference between feasible and impossible.
Write a recursive algorithm computing the nth Fibonacci number, prove it terminates and prove it correct. Then count the calls it makes: the naive version makes exponentially many — about φⁿ, the root “Solving Linear Recurrences” finds for this recurrence — and “Dynamic Programming” in Algorithms and Complexity returns to fix that.
Something to change about this lesson? Write it down, tick it off when it is handled, and export the list when you want to act on it. Kept in this browser only: nothing is sent anywhere, and nobody is notified.
No recommendations yet for this lesson.