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

Pictorial Loop Invariants

Goals

This laboratory exercise applies the concept of loop invariants to problems involving array structures.

Part A: Partitions, Medians, and Quicksort

Several useful applications utilize a function

   int partition (int a[], int left, int right)

This function rearranges the elements of a between a[left] and a[right], so that one array element has moved to the place it would belong in a sorted array. That is, the array elements are permuted to achieve the property described by the following array picture:

Partition Problem 1

As suggested by this picture, partition moves an element of a to a position a[middle] and rearranges the remainder of the array segment a[left] ... a[right], so that

As it finishes, function partition returns the final value of middle.

Thus, the array segment a[left] ... a[right] is rearranged as required, to give a new arrangement of values a[left] ... a[(middle)] ... a[right], where all array elements before position middle have values <= a[(middle)] and where a[(middle)] is less than or equal to all array elements after position middle.

In its simplest form, the designated element within partition begins at a[left]. To accomplish the final arrangement shown above, we develop a loop that maintains the following picture:

Partition Problem 2

To clarify this diagram, the variable r_spot gives the location, so that a[r_spot+1], ..., a[right] are all >= a[left], and a[left+1], ..., a[l_spot-1] are all <= a[left].

The main work in partition is to narrow the array segment in the middle of the above diagram, until there are no elements left in the middle. The main idea is to move r_spot to the left to find a small element and l_spot to the right to find a large element. Then we can swap the elements at positions l_spot and r_spot to expand the array segments for the small and large elements. Once the middle section has been eliminated, we swap a[left] and a[r_spot] to obtain the diagram at the beginning of this lab.

Adding some detail, partition proceeds with single pass through the elements a[left] ... a[right], as follow:

  1. move left in the array through a[right], a[right-1], ... until finding an element a[r_spot] where a[r_spot] < a[left], if such an element exists.

  2. move right in the array through a[left], a[left+1], ... until finding an element a[l_spot] where a[l_spot] > a[left], if such an element exists.

  3. swap a[l_spot] and a[r_spot].

  4. continue steps a, b, and c until searching all elements in the array segment. (At this point, "small" values will have been moved early within the array segment, while "large" values will have been moved late.)

  5. place a[left] in its appropriate position a[middle], where middle is the index where l_spot and r_spot have come together.

The first work for this lab asks you to implement partition.

  1. Use the in-progress diagram above to determine the exit condition for your loop, and explain why this condition is correct.

  2. Write the partition code based on this diagram, and test it with several cases.

Finding Median Values:

The partition method may be used to find the kth smallest element in an array segment a[left], ..., a[right], as follows:

  1. If there is only one element in a[left], ..., a[right], then one expects k = 1, and one can return that element.

  2. Use the partition procedure to rearrange a[left], ..., a[right] into a[left] ... a[(middle)] ... a[right] as described in problem 1.

  3. If k = ((middle)-left+1), then a[middle] is the desired element, and stop.

  4. If k <= ((middle)-left), then apply this algorithm recursively to find the kth smallest element in an array segment a[left], ..., a[(middle)-1].

  5. If k >= ((middle)-left), then apply this algorithm recursively to find the k-((middle)-left)-1th smallest element in an array segment a[(middle)+1], ...,a[right].

This discussion leads to the following coding for this lab.

  1. Write a procedure

    
    int select (int a[], int n, int k)
    

    that uses the above algorithm and procedure partition to find the kth smallest element in an array a, where a has n elements.

  2. Write a procedure

    
    int median (int a[], int n)
    

    that uses the above algorithm and procedure partition to find the median element in an array a, where n is the size of the array.

Quicksort:

The quicksort algorithm proceeds by applying partition recursively, until all subintervals have no more than a single element (and thus are already sorted).

  1. Write a procedure

    
       void quicksort (int a[], int n)
    

    that uses the partition procedure and sorts the array a[0], ..., a[n-1] using the quicksort algorithm. Since this quicksort is to be a general sorting procedure for any array, the integer n is used to indicate the size of the array.

    Include your quicksort procedure in an appropriate test program, so that you can adequately check the correctness of your code.

Part B: Dutch National Flag Problem

The Dutch National Flag Problem was first proposed by W.H.J. Feijen and made famous by Edsger W. Dijksra. The following formulation relates the problem to arrays in C.

Enumerations in C are described in Kernighan and Ritchie, Section 2.3, page 39. For example, an enumeration with three colors could be declared as:

typedef  { red, white, blue } color;

and we may consider an array of colors:

   #define size 50 /* number of elements in an array */
   color colors [size];

Program dutch-flag-start.c contains a shell for this program.

Color Array

When we begin, we do not know the number of elements of each color, and we are not even assured that each color is actually present.

The Dutch National Flag Problem seeks to sort this array, so that red's come first, then white's, and then blue's. Movement of array elements may be accomplished only by swapping two items.

Sorted Color Array

Solution

Although one approach to this problem involves simple sorting (just consider red < white < blue), the problem can be solved in a single pass of the data. The idea is to identify an array diagram that describes sections of colors as loop invariants. Writing the code then is reasonably straightforward; we just have to maintain the invariant!

For this problem, at least four different pictorial loop invariants initially come to mind:

Possible Loop Invariants

In each case, we must introduce variables to keep track of the edge of the red, white, and blue sections. Initially, these sections contain no elements, and the entire array is unprocessed. Then as processing proceeds, the program looks at successive unprocessed elements and puts them in their correct locations — maintaining the loop invariant.

  1. For two of these pictorial loop invariants, introduce variables to record the index of a boundary between colors, and describe the invariant carefully in words. Then add the variables to the pictorial loop invariants. Check with your instructor before continuing!

In what follows you will create two loop segments to solve the Dutch National Flag Problem, one segment for each invariant you have identified above.

  1. For each of the two approaches, initialize your variables, so that the pictorial loop invariants are satisfied at the start of processing.

  2. Complete the loop processing, maintaining the identified loop invariant.

  3. Test your program with several test runs.


This document is available on the World Wide Web as

http://www.cs.grinnell.edu/~walker/courses/161.sp10/labs/lab-loop-inv-pic.shtml

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