Loop Invariant Extra Exercises
Loop Invariant Extra Exercises
Jim Glenn
1
Problem 1 Prove with a loop invariant that the the following function correctly sums the
elements in the array passed to it.
Preconditions:
Pn−1
Postcondition: sum = k=0 A[k]
Pi−1
(a) sum = k=0 A[k] (that is, sum is the sum of the first i elements of A)
(b) i is an integer such that 0 ≤ i ≤ n
Basis/Initialization:
Induction: Suppose the invariant is true before one iteration of the loop and the guard i < n
is true.
Piold −1
(a) Since the invariant is true before the loop, we have sumold = k=0 A[k]. The first
Piold −1
statement inside the loop sets sumnew = sumold + A[iold ] = k=0 A[k] + A[iold ] =
Piold
k=0 A[k]. The second statement sets Pinew = iold + 1. Substituting that into the result
new −1
of the first statement gives sumnew = ik=0 A[k], which is part (a) of the invariant
with the new values of the variables.
2
(b) By the invariant, iold is an integer such that 0 ≤ iold ≤ n. Since the guard is true,
iold < n, which is equivalent to iold ≤ n−1 since i and n are integers. So 0 ≤ iold ≤ n−1
and 0 ≤ iold < iold + 1 ≤ n. Since the second statement in the loop sets inew = iold + 1,
we can rewrite that as 0 ≤ inew ≤ n, which is part (b) of the invariant with the new
values of the variables.
Termination: i increases each time through the loop, so eventually i ≥ n and the guard
becomes false to terminate the loop.
Postcondition: The falsity of the guard when the loop terminates means that i ≥ n. Part
(b) of the invariant says i ≤ n. The only way i ≥ n and iP
≤ n can both be true is if i = n.
n−1
Plug that into part (a) of the invariant yields sum = k=0 A[k], which is the required
postcondition for the function.
Problem 2 Prove that insertion sort is correct, using the invariant from class.
InsertionSort(A, n)
i = 1
while i < n
insert A[i] into correct location among A[0], ..., A[i-1]
i = i + 1
The ”insert...” line is either an inner loop or, as we will treat it, a call to a function insert(A,
i) that, given an array with the first i elements in sorted order, modifies A so that its first
i + 1 elements are unchanged but are reordered to be sorted, and does not change any of the
other elements in A (so insert has preconditions 1) A is a non-empty array of real numbers,
2) 0 ≤ i < len(A), and 3) A[0] ≤ · · · ≤ A[i − 1]; and postconditions 1) A[0] ≤ · · · ≤ A[i],
2) those 1st i + 1 elements are unchanged but may be reordered, and 3) the elements after
them are unchanged). (Fun exercise: write insert and prove that it is correct using a
loop invariant. This is how we tackle nested loops: treat the innermost loop as a separate
function, prove that it does what it is supposed to do, and then work on the next outer
loop.)
Preconditions:
3
Postcondition: A[0] ≤ · · · ≤ A[n − 1] and A has the same elements as before (but possibly
in a different order).
Invariant:
Solution Basis:
Induction: Suppose the invariant is true before an iteration of the loop and that the guard
i < n is also true.
(a) By part (a) of the invariant, A[0]old ≤ · · · ≤ A[iold − 1]old , and by part (d) and the
guard, 0 ≤ iold < n. Those are the preconditions for the call insert(A, i), so after
that call we have A[0]new ≤ · · · ≤ A[iold ]new by the postconditions of insert. By the
last statement in the loop, we have inew = iold + 1, so we can rewrite the previous
results as A[0]new ≤ · · · ≤ A[inew − 1]new , which is part (a) of the invariant using the
new values of the variables.
(b) By part (b) of the invariant, A[0]old , . . . , A[iold −1]old are the original first iold values from
A in some order. From part (c), A[iold ]old is its original value. Now the postcondition
of insert says those elements are reordered but not changed, so the new values are
the original first iold + 1 = inew values from A in some order, which is part (b) of the
invariant with the new values of the variables.
(c) By part (c) of the invariant, A[iold + 1], . . . , A[n] are their original values. There are no
assignments to them in the loop, and the postcondition of insert guarantees that they
are not changed. So they still hold their original values. Rewriting using inew = iold + 1
yields A[inew ], . . . , A[n] have their original values, which is part (c) of the invariant
using the new values of the variables.
4
(d) 0 ≤ iold ≤ n by part (d) of the invariant. iold < n since the guard is true and
hence iold ≤ n − 1 since i and n are integers (by the part (d) of the invariant and the
precondition on n respectively). Therefore, 0 ≤ iold < iold +1 ≤ n. Since inew = iold +1,
we can rewrite that as 0 ≤ inew ≤ n, which is part (d) of the invariant with the new
values of the variables.
Termination: i increases each time through the loop, so eventually we have i ≥ n, which
breaks the loop.
Postconditions: When the loop terminates, we have i ≥ n from the guard being false, and
i ≤ n from part (d) of the invariant, so i = n at termination. Substituting n for i into parts
(a) and (b) of the invariant then yields A[0] ≤ · · · ≤ A[n − 1] and A[0], . . . , A[n − 1] are their
original values, reordered, which are the postconditions for sorting A.
Now that we’ve done some work with loop invariants including conditions on the loop coun-
ters, let’s cut that tedious part out. If there is a variable x that is initialized to x = c1 before
the loop, the guard on the loop is x < c2 for some c2 >= c1 , c1 and c2 are integers, and
the only assignment to x inside the loop is x = x + 1 (which is not in any other inner loop
or conditional), then we may conclude that x is always an integer such that c1 ≤ x ≤ c2 ,
and that the loop terminates when x = c2 . (Corollary: under the same conditions, but with
guard P ∧ x < c2 , then the conditions on x still hold, but at the termination of the loop all
we know is ¬P ∨ x = c2 ).