CSC 161 Grinnell College Spring, 2010
 
Imperative Problem Solving and Data Structures
 

Introduction to Loop Invariants

Goals

This laboratory exercise introduces the concept of loop invariants and provides some practice in using loop invariants in developing programs.

Part 1: Using Loop Invariants to Analyze Existing Code

  1. Check your understanding of the term loop invariant by explaining the difference among the terms loop invariant, loop-continue condition, and exit condition.

Consider the following problem from the reading.

Problem: Read a (nonzero) number r from the keyboard and compute (and print) r0, r1, r2, r3, ..., r10.

The reading specifies three solutions to this problem, and these solutions are given in program loop-invariants-1.c

  1. Copy loop-invariants-1.c to your account. Then compile and run the program.

Review the following code segment from the reading.

   
  printf ("First Solution\n");
  prod = 1;
  i = 0;
  while (i <= 10) {
    printf ("\t%6.2lf", prod);
    prod *= r;  
    i++;
  }
  printf("\n");
  1. For this Code Segment 1, state a loop invariant and state both loop-continue and exit conditions. (Remember: loop invariants are always true; exit conditions are true only when the loop stops; and loop-continue conditions are true only when the loop continues to execute. Thus, loop invariants, loop-continue conditions, and exit conditions must be different!)

Next, review Code Segment 2:


  printf ("Second Solution\n");
  printf ("\t%6.2lf", 1.0);
  prod = 1; 
  i = 0;  
  do {
    i++; 
    prod *= r;
    printf ("\t%6.2lf", prod);
  }
  while (i < 10);                        
  printf("\n");
  1. This code is different from the previous code segments, in that the statements i++; and prod *= r; come before printing here, whereas they come after printing in the approach 1. Explain why the order of these statements is different here from the earlier examples, basing your comments on the differences in the loop invariants.

Last in this group of solutions is Code Segment 3:


  printf ("Third Solution\n");
  printf ("\t%6.2lf", 1.0);
  prod = r;
  printf ("\t%6.2lf", prod);
  i = 0;
  while (i < 9) {
    i++;
    prod *= r;  
    printf ("\t%6.2lf", prod);
  }
  printf("\n");
  1. Review Code Segment 3, and write careful statements updating the previous Statements B, C, and D for these new loop invariant from the reading.

Part 2: Using Loop Invariants for Problem Solving

Consider the following problem, which is a variation of the previous problem.

Problem 2: Read a number r from the keyboard and compute (and print) r10. In your computation, use successive multiplications in your computation rather than relying upon any built-in functions or library functions.

Note that Problem 2 asks you to compute the same final value as Problem 1, but here the smaller powers of r are not to be printed.

Using the previous program in this lab as motivation, we can approach this problem in several ways, with variables i and prod keeping track of where we are within a computation. Finally, when our computation is complete, we can print the result. In each case, details of the work will depend upon the particular loop invariant chosen.

Following Part 1's Code Segment 1, we might chose our loop invariant to be:

  1. At the start of each time through the loop, i represents a current exponent of r and prod represents ri.

Such an invariant might suggest code of the general form:


  printf("First Approach\n");
  prod = ??;
  i = ???;
  while (i <= ????)
    { prod *= r;
      i++;
    }
  printf("%6.2lf\n", prod);
  1. Fill in the question marks in a way consistent with the above loop invariant and in a way that generates the appropriate final value for prod. Place your code in a program shell to demonstrate that your code works correctly.

  2. Explain why your initialization and loop-continue condition are consistent with this loop invariant.

  3. Use the loop invariant and your loop-continue condition to explain why your code generates the correct result.

A second solution to Problem 2 uses the same loop invariant in the context of a do-while loop, paralleling Code Segment 2 from Part 1.


  printf("Second Approach\n");
  prod = ??;
  i = ???;
  do
    { i++;
      prod *= r;
    }
  while (i < ????);
  printf("%6.2lf\n", prod);
  1. Fill in these question marks, again based upon this loop invariant, and test the resulting code in a program.

  2. Explain the correctness of your code as before, basing your answer on your loop invariant and loop-continue condition.

A third solution might use the loop invariant for the earlier Code Segment 3:

  1. At the start of each time through the loop, i represents the number of multiplications of r within itself to obtain the product prod.

Such an perspective might yield code of the following form:


  printf("Third Approach\n");
  prod = ??;
  i = ???;
  while (i < ????)
    { i++;
      prod *= r;
    }
  printf(prod"%6.2lf\n", );
  1. Again, fill in appropriate values for these questions marks, based on this new loop invariant, and test your code.

  2. Paralleling your previous work in this part, explain why the resulting code is correct.

Part 3: Finding Program Errors (When They Exist) Using Loop Invariants

Consider the problem of reading successive numbers from the keyboard, until a non-positive value is read, and finding the average of the positive values. For example, if the numbers 1, 2, 3, 4, 5, -1 are entered, then the average of 1, 2, 3, 4, 5 is computed, and the resulting value (3.0) is printed.

Program loop-invariants-2.c shows five code segments that attempt to solve this problem, all assuming that at least one non-negative value will be entered.

  1. Review each of these code segments, following the same type of reasoning used Parts 1 and 2 of this lab. Indicate in general terms what is true each time through the loop. What is the idea of the loop? If the loop is working toward a goal, how does each iteration of the loop work toward that goal? How would you describe the value stored in variable sum at the start of each iteration of the loop?

  2. For each loop, write a statement of what is true at the beginning of the each time through the loop. Describe what value each variable is supposed to have at the start of each loop iteration. That is, write a loop invariant describing the value each variable should have at the start of each time through the loop.

  3. In considering each of these code segments and loop invariants, which of these code segments will give the correct average when run for any data set (as long as the data begin with a non-negative number)?

  4. Determine how the flawed code segments above might be corrected.

    1. How could a loop invariant be changed, so sum and count have the correct values at the end of the program?}
    2. How should the code itself be modified?
    3. Check your conclusions to the above questions by including each piece of code in a sample program and testing the result with test data.

Work To Turn In:


This laboratory exercise is based on part of an on-going project of introducing the concepts of assertions and loop invariants informally in CS1 and CS2 courses. Early funding for this work came, in part, from NSF Grant CDA 9214874, "Integrating Object-Oriented Programming and Formal Methods into the Computer Science Curriculum". Henry M. Walker worked as Senior Investigator on this portion of that effort.


This document is available on the World Wide Web as

http://www.cs.grinnell.edu/~walker/courses/153.sp10/labs/lab-loop-invariants.shtml

created 25 October 2007
last revised 27 February 2010
Valid HTML 4.01! Valid CSS!
For more information, please contact Henry M. Walker at walker@cs.grinnell.edu.