Initialisation is the base case
The invariant must hold before any iteration has run, which is usually immediate and is usually where an off-by-one hides.
A loop invariant is a statement true before the loop and preserved by every iteration. Together with termination it proves the loop computes what it should — and the argument is exactly induction.
Core ideas
The three parts of a loop-invariant proof are the three parts of an induction proof, renamed.
The invariant must hold before any iteration has run, which is usually immediate and is usually where an off-by-one hides.
Assume the invariant before an iteration and prove it after. The loop body is what you reason about.
The invariant plus the negated loop condition should give exactly the postcondition. If it does not, the invariant is too weak.
The material
The method, three worked loops, and how to find an invariant.
A loop invariant is a statement that is true immediately before the loop begins and remains true immediately before each subsequent iteration.
Initialisation. Before the loop, i = 0 and s = 0; the sum of the first zero elements is 0, so the invariant holds vacuously.
Maintenance. Suppose s = A[0] + ⋯ + A[i−1] at the top of an iteration. The body adds A[i] to s and increments i, so afterwards s = A[0] + ⋯ + A[i−1] with the new i — the invariant again.
Termination. The loop exits with i = n, and the invariant then reads s = A[0] + ⋯ + A[n−1], which is the whole sum. That the loop exits follows because i increases by 1 each iteration and is bounded above by n.
Initialisation: at j = 1 the subarray A[0..0] is one element, trivially sorted. Maintenance: the inner loop shifts elements greater than key one place right and inserts key in the gap, so A[0..j] ends sorted and holds the same elements. Termination: the outer loop ends with j = n, so A[0..n−1] is sorted — which is the postcondition.
The invariant says two things: sorted, and the same elements. Dropping the second half would leave the proof compatible with an algorithm that sorts by replacing the array with zeros. Invariants that are too weak are the usual failure, and they fail at the termination step.
Partial correctness is: if the loop terminates, the postcondition holds. Total correctness adds that it does terminate. An invariant gives the first; a decreasing bounded quantity gives the second.
The distinction matters. A loop with a correct invariant that never exits is partially correct and useless. Termination is proved the same way as for a recursion: exhibit a quantity that strictly decreases and cannot decrease forever.
Interactive
Counts from running, not from quoting
Method
State the invariant before writing anything else.
What must be true when the loop finishes. Without it there is nothing to aim the invariant at.
Usually the postcondition with the loop variable substituted. Check it holds before any iteration has run.
Assume the invariant at the top of an iteration, follow the body, and show it holds at the top of the next.
A decreasing bounded quantity ends the loop; the invariant plus the negated loop condition must give the postcondition. If it does not, strengthen the invariant.
“The Euclidean Algorithm” in Number Theory and Cryptography uses this; here it is the correctness argument.
The invariant is the entire proof: the algorithm works because the quantity it is computing never changes while the numbers get smaller. Every good invariant has that character — it names what the loop preserves, which is more informative than what the loop does.
Write a loop computing xⁿ by repeated multiplication and state its invariant. Then check that at exit the invariant plus the loop condition gives exactly result = xⁿ — if it needs an extra sentence, the invariant was too weak.
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.