Invariants 101

The Theory

Loop Invariant: A loop invariant is a boolean expression (i.e. a description of the state) that you, the programmer, expects to be true at the start of each loop iteration.

For simplicity, we'll assume that we are dealing with a while loop and that the loop body contains no break statements.

S
/* A */
while( /*B*/ G ) {
    /*C*/
    T
    /*D*/
}
/*E*/

The only way out of the loop is if the "guard expression" G is false. For simplicity, I'll assume that the expression G does not change the state of the program.

S is a sequence of statements called the "loop initializaton"

T is a sequence of statements called the "loop body".

A, B, C, D, and E are points in the execution of the code.

When we talk about the state at point B, we mean the state just before the guard expression G is evaluated. Since the the guard expression is assumed not to change the state, this is the same as the state just after the guard expression is evaluated.

Let's assume now that there P is a boolean expression with the following properties

  1. Executing S ensures that P will be true at point A
  2. If P and G are assumed to be true at point C, then P will also be true at point D.

We call P the "loop invariant".

Then we can observe the following

  1. On the first iteration of the loop, P will be true at point B, because of assumption 1.
  2. On the first iteration of the loop, P will be true at point C, because of the assumption that evaluating G does not change the state and observation 3.
  3. On the first iteration of the loop, P will be true at point D, because of assumption 2 and observation 4.
  4. On the second iteration of the loop, P will be true at point B, because of observation 5.
  5. On the second iteration of the loop, P will be true at point C, because of the assumption that evaluating G does not change the state and observation 6.
  6. On the second iteration of the loop, P will be true at point D, beacause of assumption 2 and observation 7.
  7. and so on ad infinitem

So, by induction, we can show that P will be true every time the program reaches point B.

Now consider what happens when the guard is false. The program jumps to point E and we can observe that, because P was true at point B and beacuse G does not change the state:

Strategy for coding loops

Suppose we want to construct a loop with the goal of getting the state such that some boolean expression R is true.

We try to find boolean expressions P and G such that

(P and not G) implies R

We use G as the loop's guard and P as its invariant

Now:

Now let's apply this idea to some practical examples.

Example: Finding an item known to be in an array

Suppose A is an array of length N and we know that there is at least on occurence of a value x in the array. We want to find the location of the first occurrence of x in the array.

The final state we want can be described by a conjunction

We can take the inverse of the last conjunct as a loop guard, G, and the remaining conjuncts as an invariant.

Now an outline of the solution is

ensure P
while( A[i] != x ) {
    assuming P and G, ensure P
}

We can ensure P by setting i to 0. Thus we have

int i = 0 ;
// Invariant:
//   * 0 £ i < N and
//   * (for all 0 £ j < i, A[j] != x)
while( A[i] != x ) {
    assuming P and G, ensure P
}

At the start of the loop body we know that A[i] is not x, so we can safely increment i without invalidating the loop invariant.

Extra Note The argument is a little more subtle than that. We also need to be sure that i+1 < N. Since i<N (from the invariant), we know that i+1 £ N. From the assumption that there is at least one x in the N entries in the array and the fact (from the invariant) that x isn't in the first i+1 entries, i+1 can not equal N. Thus i+1 < N. End Note.

int i = 0 ;
// Invariant:
//   * 0 £ i < N and
//   * (for all 0 £ j < i, A[j] != x)
while( A[i] != x ) {
    i += 1 ;
}

Example: GCD

We will calculate the greatest common divisor of two positive numbers A and B.

Some facts about gcd

We will use two local variables x and y.

Our goal is that x == gcd(A,B).

We know that the gcd of a number and itself is that number, thus the following conjunction implies the goal.

We take the first three conjuncts as the invariant and the inverse of the second as a guard.

We can establish the invariant easily

int x = A ;
int y = B ;
// Invariant:
//   * x > 0 and y > 0, and
//   * gcd(x,y)==gcd(A,B)
while( x != y ) {
    assuming P and G, ensure P
}

We need to change x and or y so that the gcd(x,y) remains the same.

We use the property that

gcd(x,y) == gcd(x,y-x), for all x,y, with 0 < x < y

to show that the assignment

y = y-x;

preserves the invariant, if 0 < x < y. And we can use the property that

gcd(x,y) == gcd( x-y, y), for all x, y, with 0 < y < x

to show that the assignment

x = x-y;

preserves the invariant, if 0 < y < x. Thus we can use the following loop body

if( x < y )
    y = y-x ;
else
    x = x-y ;

The final loop is

int x = A ;
int y = B ;
// Invariant: x > 0 and y > 0 and gcd(x,y)==gcd(A,B)
while( x != y ) {
    if( x < y )
        y = y-x ;
    else
        x = x-y ;
}

Termination

We should check that the loop will indeed terminate.

One way to do this is to identify an integer expression that is always nonnegative and that decreases with each iteration of the loop. Obviously such an expression can not exist if the loop never terminates.

Variant: Such an expression is called a "variant".

In this example we can use the expression

x+y

as the variant. From the invariant, we know x and y are always positive, and thus x+y is nonnegative. The loop body obviously decreases this sum (again using the fact that x and y are both positive.

Example: Sorting an array

Suppose A is an array of ints of length N.

We wish to sort it into ascending order.

Goal: Our goal that at the end of the loop.

We can take as an invariant:

Then the goal is implied by the invariant, together with i==N.

So as a guard we use

i < N

We can establish the invariant using a declaration:

int i = 0 ;

We need a loop body which preserves the invariant, assuming, initially, that i < N.

We can do that by setting A[i] to the smallest remaining element, while keeping A a permutation of its original value.

int i = 0 ;

//Invariant:
//  * £ i £ N, and
//  * the smallest i members of A are in the first i positions of A, and
//  * the first i members of A are in ascending order, and
//  * A is a permutation of its original value
while( i < N ) {
    Let j be the smallest item in A between
    positions i and N-1 (inclusive).
    Swap A[i] with A[j].
    i += 1 ; }

Question: Can you suggest a variant for this loop?

Another search

In the last problem we were left with the problem of finding the smallest item of a part of an array.

The goal is to Let j be the smallest item in A between positions i and N-1 (inclusive).

We can assume that i<N.

Let's use variable k such that i < k £ N.

Then we can use as an invariant

Now we can establish the invariant using the declarations

int j = i ;
int k = i+1 ; 

The guard of k < N will ensure that the goal is true at the end of the loop. Now the invariant can be preserved by the loop body

if( A[k] < A[j] ) j = k ;
k += 1 ;

Putting these parts together gives

int j = i ;
int k = i+1 ;
// Invariant:
//   * i < k £ N, and
//   * j is the index of the smallest item
//     between positions i and k-1 (inclusive) 
while( k < N ) {
    if( A[k] < A[j] ) j = k ;
    k += 1 ; }

 

 


© Theodore S. Norvell 1996--2004.