Introduction to Programming (CPSC 124)
—Hobart & William Smith Colleges, Fall 2014
Class Notes—September 24, 2014
Home | Syllabus | Calendar | Class Notes | Labs and Projects | General Notes |

Loop patterns

There are certain patterns of iteration that occur in a wide variety of algorithms. Here are some particularly useful ones:

Loop component checklist

Loops that terminate have four components:

<initialization of variables>     // (1)
while ( <test> ) {                // (2)
    <main part of the loop body>  // (3)
    <progress toward termination> // (4)
}

The "progress" statement (4) is the component of the loop body that eventually makes the test (2) evaluate to false. It is often distinct from the "main body" (3) part, but it doesn't have to be. The important part is to make sure you understand where it is in a loop body and that it actually does progress toward making the <test> false.

Example: Compound Interest

The following code fragment calculates the value of an investment (principal) after y years, at an annual percentage rate apr

double principal = Double.parseDouble(args[0]); double apr = Double.parseDouble(args[1]); int y = Integer.parseInt(args[2]); // we'll assume y is positive int i = 0; while (i < y) { principal = principal * (1.0 + apr); i = i + 1; }

Initialization is done with the four declarations before the loop, since the loop uses all four variables. The main part of the body is the update to principal. The progress is the increment of i, since (assuming y is positive), the loop will eventually increase i enough that (i < y) is false.

Accumulator pattern: incrementally build a "running total"


total = < initial value > ;

while ( < some test > ) {

    < first part of loop body >
    
    total = total < OP > < incremental improvement > ;
    
    < rest of loop body >
}

The compound interest code above is also an example of this pattern: principal is initially whatever value the user sets, and on each iteration of the loop, we update it with the interest calculated from another year.

Another example that we did in class is the calculation of n! = x × (n-1) × ... × 1, for some integer n. The "running total" here is kept in nF:

public class Fact { public static void main(String[] args) { int n = Integer.parseInt(args[0]); int nF = 1; int i = 1; while (i <= n) { nF = nF * i; i = i + 1; } System.out.println(nF); } }

The third example we did in class was the calculation of integer powers. Given a integers a and b, print the value of ab:

public class IExp { public static void main(String[] args) { int a = Integer.parseInt(args[0]); int b = Integer.parseInt(args[1]); int result = 1; int i = 0; while (i < b) { result = result * a; i = i + 1; } System.out.println(result); } }

Counting ("definite loop") pattern: run a loop body exactly n times

int i = 0;

while (i < n) {
    < main part of the body >
    i = i + 1;
}

There are several variants on this. For example, we initialize i to 1 instead of 0 and write our while test as "(i <= 1)". Or we can initialize i at n, use the test "(i > 0)", and count down with "i = i - 1;".

There are examples of this pattern all over. In these notes all three programs—compound interest, Fact, and IExp incorporate counting patterns.

More examples

ASCII Art

Given an integer n, display a n × 2n rectangle, made of * characters:

public class Rect { public static void main(String[] args) { int n = Integer.parseInt(args[0]); int row = 0; while (row < n) { int col = 0; while (col < 2*n) { System.out.print("* "); col = col + 1; } System.out.println(); row = row + 1; } } }

This contains two examples of counting, one for the inner loop and one for the outer. It is also an example of a nested loop, in which the inner loop runs through a complete cycle of 2n iterations once for every iteration of the outer loop.

Calculating the square root of a number:

An example of the accumulator pattern, though it's probably hard to see it. The "improvement" is from mathematics developed by Isaac Newton:

public class Sqrt { public static void main(String[] args) { double x = Double.parseDouble(args[0]); System.out.println("sqrt(" + x + "): "); double sqX = 22.3; // "guess" while (!(Math.abs(sqX*sqX - x) < 0.0000000001)) { sqX = (sqX + (x / sqX)) / 2.0; } System.out.println(sqX); } }

Approximating the value of π

The number π = 3.141592653589793 ... can be approximated by the series

4 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + 4/13 - ... 
= 4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + 1/13 - ... )

With infinitely many terms in this series, you get π exactly. In class, I showed a program that calculated this approximation by computing n terms of the series, where the user specifies the value of n:

public class piApprox { public static void main(String[] args) { int n = Integer.parseInt(args[0]); double pi = 0.0; int denom = 1; int sign = 1; int i = 0; while (i < n) { pi += (sign * (1.0/denom)); denom += 2; sign = sign * -1; i = i + 1; } pi = pi * 4.0; System.out.println(pi); System.out.println(Math.PI); } }

You can see the counting pattern at work here. There's also an accumulation going on (the running total is kept in pi). This one is mainly a curiosity, rather than a practical program, as n has to be very large (at least a million) before this approximation is any good.


John Lasseter