We write loops to accomplish some task, for example, given an array of integers, to find the largest
element in the array. The (loop) invariant states what is true at each iteration of the loop
(before the test is made). The invariant should be strong enough to allow us to show that the
loop accomplishes its intended task.
One can get very formal about (loop) invariants and, in general, about how to show that a program
is correct. Our goals here are very modest: just to learn to better understand what we do
when we write a loop. We will proceed by examples. I write the invariants in precise english
and I give arguments to try convince you that they are valid and useful invariants. If you find the
arguments difficult or boring, then skip them, but try at least to understand
what each invariant tries to say. And try to think of your programs more in terms of ideas
expressed in english than in terms of details of programming statements. We need to become able to reason
about what our programs do.
/* return largest value in array a with n elements.*/ int findmax(const int a[], int n) { int who = a[0]; // Represents the largest value found // up to now in a. for (int k = 0; k < n; ++k) { // INVARIANT: for all positions j from 0 to k-1 // a[j] <= who, thus who is the largest element in the range 0..k-1 if (a[k] > who) who = a[k]; } return who; }Is the invariant that we have indicated, for all positions j from 0 to k-1, a[j] <= who, thus who is the largest element found in the range 0..k-1, truly a good loop invariant?
int linearSearch(const int a[], int n, int who) { for (int k = 0; k < n; ++k) { // INVARIANT: for all positions j from 0 to k-1, who != a[j] if (a[k] == who) return (k); } return (-1); }The arguments in this case are similar to those used in Example 1.
int binarySearch(const int a[], int n, int who) { int low = 0; int high = n-1; while (low <= high) { // INVARIANT: if who is in the array a, it is in the range low..high. int mid = (low+high)/2; if (who < a[mid]) high = mid-1; else if (who > a[mid]) low = mid+1; else return (who); } return (-1); }Clearly the invariant is true the first time the loop is entered, since at that time the range low..high is for the whole array. Assume now that who is in the range low..high. If who < a[mid], since a is sorted, if who is in a it will be to the left of mid, thus in the range low..mid-1, that is in low..high in the next iteration. We reason similarly if who>a[mid] with the same result. If who==a[mid] then there is no next iteration, we just return as promised the position where who was found, mid.
/* Given array a with n elements, sort it in non decreasing order, using the selection method */ void selectionSort(int a[], int n) { for (int limit=n; limit>1; limit--) { // INVARIANT1: All elements in range limit..n-1 are in their final // sorted position. int w = 0; for (int k=1; k < limit; ++k) // INVARIANT2: a[w]>=a[j] for all j in range 0..k-1 if (a[w] < a[k]) w = k; int temp = a[w]; a[w] = a[limit-1]; a[limit-1] = temp; } }In selectionSort we have two loops, thus two invariants.