Iteration: Chapter Goals
Iteration: Chapter Goals
Iteration: Chapter Goals
6
CHAPTER
Chapter Goals
◆ To be able to program Loops with the while, for, and do statements
◆ To learn how to read input from the console and from a file through redirection
◆ To implement simulations
223
224 Chapter 6. Iteration
Recall the investment problem from Chapter 1. You put $10,000 into a bank account
that earns 5 percent interest per year. How many years does it take for the account
balance to be double the original?
In Java, the while statement implements such a repetition. The code
while ( condition )
statement
keeps executing the statement while the condition is true. Most commonly, the state-
ment is a block statement, that is, a set of statements delimited by { . . . }.
Here is the program that solves our investment problem:
Program DoubleInv.java
public class DoubleInv
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Interest rate:");
double rate = console.readDouble();
System.out.println("Initial balance:");
double initialBalance = console.readDouble();
int year = 0;
double balance = initialBalance;
Java Syntax
6.1 The while Statement
while (condition)
statement
Example:
while (balance < 2 * initialBalance)
{ year++;
double interest = balance * rate / 100;
balance = balance + interest;
}
Purpose:
To execute a statement while a condition is true
Figure 1
balance < 2 * No
initialBalance?
Yes
Add interest;
year++
Print year
226 Chapter 6. Iteration
executes the body over and over, without ever terminating. Whoa! Why would you
want that? The program would never stop. There are two reasons. Some programs
indeed never stop; the software controlling an automated teller machine, a tele-
phone switch, or a microwave oven doesn’t ever stop (at least not until the device
is turned off). Our programs are not usually of that kind, but even if you can’t ter-
minate the loop, you can exit from the method that contains it. This can be helpful
when the termination test naturally falls into the middle of the loop (see Advanced
Topic 6.3).
Far and away the most common loop has the form
i = start ;
while (i <= end )
{ . . .
i++;
}
Because this loop is so common, there is a special form for it that emphasizes the
pattern:
for (i = start ; i <= end ; i++)
{ . . .
}
You can also define the loop counter variable inside the for loop header. That is a
convenient shorthand. It restricts the use of the variable to the body of the loop.
for (int i = start ; i <= end ; i++)
{ . . .
}
Here is a program that prints out the growth of an investment over 20 years:
Program Invest.java
public class Invest
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Interest rate:");
double rate = console.readDouble();
Java Syntax
6.2 The for Statement
for (initialization ; condition ; update)
statement
Example:
for (i = 1; i <= 20; i++)
{ double interest = balance * rate / 100;
balance = balance + interest;
}
Purpose:
To execute an initialization, then keep executing
a statement and updating an expression while a
condition is true
year = 1
No
year <= 20?
Yes
Add interest
Print year,
balance
year++
Figure 2
The three slots in the for header can contain any three expressions. You can count
down instead of up:
for (year = 20; year > 0; year--)
The increment or decrement need not be in steps of 1:
for (x = -10; x <= 10; x = x + 0.5) . . .
It is possible—but a sign of unbelievably bad taste—to put unrelated conditions into
the loop:
for (rate = 6; year-- > 0; System.out.println(balance))
. . . // Bad taste
We won’t even begin to decipher what that might mean; look in Feuer [1] for puzzles
of this kind. You should stick with for loops that initialize, test, and update a single
variable.
int j = 10;
◆
for (int i = 0; i <= 10; i++)
◆ { . . .
◆ j--;
◆ }
6.3 do Loops
Sometimes you want to execute the body of a loop at least once and perform the
loop test after the body was executed. The do loop serves that purpose:
do
statement
while (condition );
The statement is executed while the condition is true. Since the condition is tested after
the statement is executed, the statement is executed at least once.
For example, suppose you want to make sure that a user enters a positive number.
As long as the user enters a negative number or zero, just keep prompting for a correct
input. In this situation, a do loop makes sense because you need to get a user input
before you can test it.
do
{ System.out.println("Please enter an interest rate (> 0):");
rate = console.readDouble();
} while (rate <= 0);
See Figure 3 for a flowchart.
Figure 3
Flowchart of a do Loop
Print prompt
Read rate
Yes
rate <= 0?
No
232 Chapter 6. Iteration
Java Syntax
6.3 The do Statement
do
statement
while (condition );
Example:
do
{ System.out.println("Please enter an interest rate (> 0):");
rate = console.readDouble();
} while (rate <= 0);
Purpose:
To execute a statement once and then keep executing it as long as a
condition is true
Like a for loop, a do loop can always be replaced by a while loop. Here we
initialize rate with an artificial value to ensure that the test passes the first time
around:
double rate = -1;
while (rate <= 0)
{ System.out.println("Please enter an interest rate (> 0):");
rate = console.readDouble();
}
This artificial choice is not good style, and it is best to use a do loop in this case.
◆ Some people try to solve off-by-1 errors by randomly inserting + 1 or - 1 until the program
◆ seems to work. That is, of course, a terrible strategy. It can take a long time to compile and
◆ test all the various possibilities. Expending a small amount of mental effort is a real time saver.
◆ Fortunately, off-by-1 errors are easy to avoid, simply by thinking through a couple of test
◆ cases and using the information from the test cases to come up with a rationale for your
◆ decisions.
◆ Should year start at 0 or at 1? Look at a scenario with simple values: an initial balance
◆ of $100 and an interest rate of 50%. After year 1, the balance is $150, and after year 2 it is
◆
$225, or over $200. So the investment doubled after 2 years. The loop executed two times,
◆
incrementing year each time. Hence year must start at 0, not at 1.
◆
In other words, the balance variable denotes the balance after the end of the year. At the
◆
◆ outset, the balance variable contains the balance after year 0 and not after year 1.
◆ Next, should you use a < or <= comparison in the test? That is harder to figure out, because
◆ it is rare for the balance to be exactly twice the initial balance. Of course, there is one case
◆ when this happens, namely when the interest is 100%. The loop executes once. Now year is
◆ 1, and balance is exactly equal to 2 * initialBalance. Has the investment doubled after
◆ one year? It has. Therefore, the loop should not execute again. If the test condition is balance
◆ < 2 * initialBalance, the loop stops, as it should. If the test condition had been balance
◆ <= 2 * initialBalance, the loop would have executed once more.
◆ In other words, you keep adding interest while the balance has not yet doubled.
◆ For floating-point values, there is another reason not to use !=: Because of roundoff errors,
◆ the exact termination point may never be reached.
◆ Of course, you would never write
◆
◆ for (rate = 5; rate != 10; rate = rate + 0.3333333) . . .
◆
◆ because it is highly unlikely that rate would match 10 exactly after 15 steps. But the same
◆ problem may happen for the harmless-looking
◆ for (rate = 5; rate != 10; rate = rate + 0.1) . . .
◆
◆ The number 0.1 is exactly representable in the decimal system, but the computer represents
◆ floating-point numbers in binary. There is a slight error in any finite binary representation of
◆ 1/10, just as there is a slight error in a decimal representation 0.3333333 of 1/3. Maybe rate is
◆ exactly 10 after 50 steps; maybe it is off by a tiny amount. There is no point in taking chances.
◆ Just use < instead of !=:
◆
◆ for (rate = 5; rate < 10; rate = rate + 0.1) . . .
◆
◆ year = 1
◆
◆
◆
◆
◆
◆ year++
◆
◆
◆
◆ Add interest
◆
◆
◆
◆
◆ balance < 2 * Yes
◆ initialBalance?
◆
◆
◆
◆ No
◆ Print year
◆
◆
◆ Figure 4
◆
◆
◆ Spaghetti Code
◆
◆
◆ double interest = balance * rate / 100;
◆ balance = balance + interest;
◆ } while (balance < 2 * initialBalance);
◆ In fact, why even bother with the do loop? Here is a faithful interpretation of the flowchart:
◆
◆ year = 1;
◆ goto a; // not an actual Java statement
◆ b:
◆ year++;
◆ a:
◆ double interest = balance * rate / 100;
◆ balance = balance + interest;
◆ if (balance < 2 * initialBalance) goto b;
◆
◆ This nonlinear control flow turns out to be extremely hard to read and understand if you have
◆ more than one or two goto statements. Because the lines denoting the goto statements weave
◆ back and forth in complex flowcharts, the resulting code is named spaghetti code.
◆ In 1968 the influential computer scientist Edsger Dijkstra wrote a famous note, entitled
◆ “Goto statements considered harmful” [2], in which he argued for the use of loops instead of
◆ unstructured jumps. Initially, many programmers who had been using goto for years were
◆ mortally insulted and dug out examples where the use of goto does lead to clearer or faster
6.4 Nested Loops 237
◆ code. Some languages offer weaker forms of goto that are less harmful, such as the break
◆ statement in Java, discussed in Advanced Topic 6.3. Nowadays, most computer scientists ac-
◆ cept Dijkstra’s argument and fight bigger battles than optimal loop design.
Let us print a table that lists the values of the powers x y for all x between 1 and 10
and y between 1 and 8:
1 1 1 1 1 1 1 1
2 4 8 16 32 64 128 256
3 9 27 81 243 729 2187 6561
4 16 64 256 1024 4096 16384 65536
5 25 125 625 3125 15625 78125 390625
6 36 216 1296 7776 46656 279936 1679616
7 49 343 2401 16807 117649 823543 5764801
8 64 512 4096 32768 262144 2097152 16777216
9 81 729 6561 59049 531441 4782969 43046721
10 100 1000 10000 100000 1000000 10000000 100000000
The basic idea is simple. You have to print ten rows:
for (int x = 1; x <= 10; x++)
{ // print table row
. . .
}
How do you print a table row? You need to print the values x 1 , x 2 , . . . , x 8 . You need
to program another loop:
for (int y = 1; y <= 8; y++)
{ int p = (int)Math.pow(x, y);
System.out.print(p + " ");
}
System.out.println();
This loop prints a table row, including the newline at the end. Putting both loops
together yields two nested loops:
for (int x = 1; x <= 10; x++)
{ // print table row
1 1 1 1 1 1 1 1
2 4 8 16 32 64 128 256
3 9 27 81 243 729 2187 6561
4 16 64 256 1024 4096 16384 65536
5 25 125 625 3125 15625 78125 390625
6 36 216 1296 7776 46656 279936 1679616
7 49 343 2401 16807 117649 823543 5764801
8 64 512 4096 32768 262144 2097152 16777216
9 81 729 6561 59049 531441 4782969 43046721
10 100 1000 10000 100000 1000000 10000000 100000000
Ugh, this looks terrible. The columns don’t line up. This is a common problem when
printing columns of numbers. To solve it, convert each value into a string and keep
adding spaces until the string has the desired length. Then print the padded string.
String pstr = "" + p;
System.out.print(pstr);
Here is the complete program:
Program Table.java
public class Table
{ public static void main(String[] args)
{ final int COLUMN_WIDTH = 10;
System.out.print(pstr);
}
System.out.println();
}
}
}
6.5 Processing Input 239
Now you have a total of three nested loops! The outer for loop executes 10 times.
In each iteration, the inner for loop runs 8 times. Therefore, a total of 80 values are
computed. For each of these values, the while loop is executed to pad the result
string.
You read the input as a string, but you want to interpret it as a number. Therefore,
you must use the Integer.parseInt or Double.parseDouble method to convert
the input data from a string to a number. Here is the program to compute the average
of a set of input data.
Program Average.java
public class Average
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Enter data.");
double sum = 0;
int count = 0;
if (count == 0)
System.out.println("No data");
else
System.out.println("Average = " + sum / count);
}
}
To supply data to the program, you type the input data, a line at a time. When you
are done typing, you must indicate to the operating system that all console input for
this program has been supplied. The mechanism for this differs from one operating
system to the other. For example, in DOS you type Ctrl+Z, and on UNIX, you type
Ctrl+D, to indicate the end of console input. This special keystroke combination is a
signal to the operating system (and not to Java) to end console input. The System.in
stream never sees this special character. Instead, it just senses the end of the input.
The readLine method returns a null string (and not a string containing a control
character) at the end of input.
Of course, typing a long set of numbers at the console is tedious and error-prone.
Productivity hint 6.1 shows you how you can prepare the input data in a file and
6.5 Processing Input 241
use input redirection to have the System.in stream read the characters from that
file. In that case you do not terminate the file with a control character, because the
operating system knows the size of the file and therefore knows where it ends.
But the operating system, not being clairvoyant, cannot know the end of keyboard
input—hence the need for the special control character. (Some ancient versions of
DOS did terminate text files on disk with a Ctrl+Z, and you will still find some “ex-
perts” who tell you that all files have a special end-of-file character at the end. This is
plainly not the case—the operating system uses the size of a disk file to determine its
end.)
◆ The other solution is to exit the loop from the middle, either by a return statement or by
◆ a break statement (see Advanced Topic 6.3). Here is an example. This loop reads input data,
◆ and the method containing the loop returns value when the end of input is encountered.
◆
◆ double sum = 0;
◆ int count = 0;
◆ while (true)
◆ { String inputLine = console.readLine();
◆ if (inputLine == null) // leave loop in the middle
◆ { if (count == 0)
◆ System.out.println("No data");
◆ else
◆ System.out.println("Average = " + sum / count);
◆ return;
◆ }
◆ double x = Double.parseDouble(inputLine);
◆ sum = sum + x;
◆ count++;
◆ }
◆ is often the topic of heated (and quite unproductive) debate. In this book, we won’t use the
◆ break statement, and we leave it to you if you like to use it in your own programs.
◆ In Java, there is a second form of the break statement that is used to break out of a nested
◆ statement. The statement break label ; immediately jumps to the end of the statement that
◆ is tagged with a label. Any statement (including if and block statements) can be tagged with
◆ a label—the syntax is
◆
◆ label : statement
◆
The labeled break statement was invented to break out of a set of nested loops.
◆
◆ outerloop:
◆ while (outer loop condition)
◆ { . . .
◆ while (inner loop condition)
◆ { . . .
◆ if (something really bad happened)
◆ break outerloop;
◆
}
◆
}
◆
◆
jumps here if something really bad happened
◆ Naturally, this situation is quite rare.
◆ Finally, there is another goto-like statement, the continue statement, which jumps to the
◆ end of the current iteration of the loop. Here is a possible use for this statement:
◆
◆ double sum = 0;
◆ int count = 0;
◆ String inputLine;
◆ do
◆ { inputLine = console.readLine();
◆ if (inputLine == null) continue; // jump to the end of the loop body
◆ double x = Double.parseDouble(inputLine);
◆ sum = sum + x;
◆ count++;
◆ // continue statement jumps here
◆ } while (inputLine != null);
◆
◆ By using the continue statement, you don’t need to place the remainder of the loop code
◆ inside an else clause. This is a minor benefit. Few programmers use this statement.
but negative numbers are not, you can use ⫺1 to indicate termination. Such a
value, which is not an actual input but serves as a signal for termination, is called a
sentinel.
The following program reads in data with a zero value as a sentinel. After
reading the input data, this program could continue to read and process a second
data set.
Program Sentinel1.java
public class Sentinel1
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Enter data (0 to finish):");
double sum = 0;
int count = 0;
// compute average
if (count == 0)
System.out.println("No data");
else
System.out.println("Average = " + sum / count);
}
}
Using a special number as a sentinel works if there is some restriction on the input. In
many cases, though, there isn’t. Suppose you want to compute the average of a data
set that may contain 0 or negative values. You can’t use ⫺13 as a sentinel, even if it is
your lucky number. That number just might occur as a legitimate value in some data
set, and you wouldn’t be so lucky. In that case, you might prompt the user ”Enter
a number, Q to finish”. But now you must be careful reading the input. If the "Q" is
encountered in a call to readDouble, then an exception is thrown, and the program
terminates. (Alternatively, with the improved behavior described in Advanced Topic
6.4, the program keeps prompting for a number.) Instead, simply read in the input as
a string and then convert it to a number if it is not "Q".
6.5 Processing Input 245
Program Sentinel2.java
public class Sentinel2
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Enter data (Q to finish):");
double sum = 0;
int count = 0;
if (count == 0)
System.out.println("No data");
else
System.out.println("Average = " + sum / count);
}
}
◆ formatted number. (We use the trim method to remove leading or trailing white space from
◆ the input string. For example, if the user enters " 3", then the Integer.parseInt method
◆ complains that the leading space is not a legal part of a number. The trim method returns a
◆ string with that space removed.)
◆
◆ int n = 0;
◆ boolean inputOk = false;
◆ do
◆ { try
◆ { String inputString = console.readLine();
◆ n = Integer.parseInt(inputString.trim());
◆ inputOk = true;
◆ }
◆ catch(NumberFormatException e)
◆ { System.out.println("Input error. Try again.");
◆ }
◆ } while (!inputOk);
◆
◆ If the call to parseInt succeeds, then the assignment inputOk = true is carried out, and
◆ the loop exits. Otherwise, the catch clause for the NumberFormatException is executed. An
◆ error message is printed, and the loop is repeated because inputOk is still false.
while (tokenizer.hasMoreTokens())
{ String token = tokenizer.nextToken();
process token
}
Alternatively, you can use the countTokens method to find out how many tokens
are in the string. Then you get the tokens as follows:
int tokenCount = tokenizer.countTokens();
for (int i = 0; i < tokenCount; i++)
{ String token = tokenizer.nextToken();
process token
}
By the way, note that it would not be a good idea to write the seemingly simpler
loop
for (int i = 0; i < tokenizer.countTokens(); i++) // Inefficient
{ String token = tokenizer.nextToken();
process token
}
This loop would call the countTokens method at the end of every loop iteration.
That is not a good idea, because countTokens is a relatively slow method—it needs
to process the entire input string to determine the number of tokens.
Here is a program that simply counts the number of words in an input file. This
program is useful if you are a writer who is paid by the word. Note that the loop
that breaks up each input line is nested inside the loop that reads the lines.
Program Words.java
import java.util.StringTokenizer;
int count = 0;
StringTokenizer tokenizer
= new StringTokenizer(inputLine);
248 Chapter 6. Iteration
while (tokenizer.hasMoreTokens())
{ tokenizer.nextToken(); // read and discard
count++; // count each word
}
}
}
Program Split.java
◆
◆ import java.util.StringTokenizer;
◆
◆
◆ public class Split
◆ { public static void main(String[] args)
◆ { ConsoleReader console = new ConsoleReader(System.in);
◆
◆ boolean done = false;
◆ while (!done)
◆ { String inputLine = console.readLine();
◆ if (inputLine == null)
◆ done = true;
◆
else
◆
{ // break input line into words
◆
◆
◆ StringTokenizer tokenizer
◆ = new StringTokenizer(inputLine);
◆ while (tokenizer.hasMoreTokens())
◆ { // print each word
◆ String word = tokenizer.nextToken();
◆ System.out.println(word);
◆ }
◆ }
◆ }
◆ }
◆
}
◆
◆ Then
◆
◆ java Split < article.txt
◆
◆
◆ lists the words in the file article.txt, one on each line. That isn’t too exciting, but it becomes
◆ useful when combined with another program: sort. You don’t yet know how to write a pro-
◆ gram that sorts strings, but most operating systems have a sort program. A sorted list of the
◆ words in a file would be quite useful—for example, for making an index.
◆ You can save the unsorted words in a temporary file:
◆
◆ java Split < article.txt > temp.txt
◆ sort < temp.txt > sorted.txt
◆
◆ Now the sorted words are in the file sorted.txt.
◆ Because this operation is so common, there is a command line shorthand for it.
◆
◆ java Split < article.txt | sort > sorted.txt
◆
◆ The split program runs first, reading input from article.txt. Its output becomes the input of
◆ the sort program. The output of sort is saved in the file sorted.txt. The | operator instructs
◆ the operating system to construct a pipe linking the output of the first program to the input of
◆ the second.
250 Chapter 6. Iteration
This is the first time you have encountered the char type. A variable of type char
can hold a single character. There isn’t a lot you can do with a single character. You
can concatenate it with a string, such as
String s;
String t = s + ch;
You can compare it against a character constant. Character constants look like string
constants, except that character constants are delimited by single quotes: 'A' is a
character, "A" is a string containing a single character. You can use escape sequences
(see Advanced Topic 1.1) inside character constants. For example, '\n' is the newline
character, and '\u00E9' is the character é.
Let us use the string traversal pattern to write a method that computes the re-
verse of a string. For example, the reverse of "Hello" is "olleH". To compute the
reverse, stick each new character before the other characters that you already found.
For example, the reverse of "Hello" is built up as
""
"H"
"eH"
"leH"
"lleH"
"olleH"
Program Reverse.java
public class Reverse
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Please enter a string:");
String s = console.readLine();
String r = "";
for (int i = 0; i < s.length(); i++)
252 Chapter 6. Iteration
{ char ch = s.charAt(i);
r = ch + r; // add ch in front
}
System.out.println(s + " reversed is " + r);
}
}
In a simulation you generate random events and evaluate their outcomes. Here is a
typical problem that can be decided by running a simulation: the Buffon needle ex-
periment, devised by Comte Georges-Louis Leclerc de Buffon (1707–1788), a French
naturalist. A needle of length 1 inch is dropped onto paper that is ruled with lines 2
inches apart. If the needle drops onto a line, count it as a hit. Buffon conjectured that
the quotient tries/hits approximates . (See Figure 6.)
Now, how can you run this experiment in the computer? You don’t actually want
to build a robot that drops needles on paper. The Random class of the Java library
implements a random number generator, which produces numbers that appear to be
completely random. To generate random numbers, you construct an object of the
Random class, and then apply one of the following methods:
Figure 6
Program Dice.java
import java.util.Random;
Figure 7 yhigh
2
Variables in a Trial of
the Buffon Needle
Experiment ylow α
The angle ␣ between the needle and the x-axis can be any value between 0 degrees
and 180 degrees. The upper end of the needle has y-coordinate
Program Buffon.java
import java.util.Random;
// print approximation of PI
On one computer I obtained the result 3.10 when running 10,000 iterations and
3.1429 when running 100,000 iterations.
The point of this program is not to compute —there are far more efficient ways
for that purpose. Rather, the point is to show how a physical experiment can be
simulated on the computer. Buffon had to drop the needle physically thousands of
times and record the results, which must have been a rather dull activity. You can
have the computer execute the experiment quickly and accurately.
Simulations are very common computer applications. All simulations use essen-
tially the same pattern as the code of this example: In a loop, a large number of
sample values are generated; the values of certain observations are recorded for each
sample; when the simulation is completed, the averages of the observed values are
printed out.
A typical example of a simulation is the modeling of customer queues at a bank
or a supermarket. Rather than observing real customers, one simulates their arrival
and their transactions at the teller window or checkout stand in the computer. One
can try out different staffing or building layout patterns in the computer simply by
making changes in the program. In the real world, making many such changes and
measuring their effect would be impossible, or at least very expensive.
◆
◆ b i r
◆ a 100 1
◆
a2 50
◆
◆ a4 25
◆ 24 a4
8
◆ a 12 a4
◆ a 16
6
◆ a32 3
◆ 2 a36
◆ 64
a 1
◆
0 a100
◆
◆
◆
◆
◆ Amazingly enough, the algorithm yields exactly a100 . Do you understand why? Are you con-
◆ vinced it will work for all values of n? Here is a clever argument to show that the method
◆ always computes the correct result. We will demonstrate that whenever the program reaches
◆
the top of the while loop, it is true that
◆
◆ r ⭈ bi ⳱ an (I)
◆
◆ Certainly, it is true the first time around, because r ⳱ 1, b ⳱ a, and i ⳱ n. Suppose that (I)
◆ holds at the beginning of the loop. We label the values of r, b, and i as “old” when entering
◆ the loop and as “new” when exiting the loop. We assume that upon entry
◆
i
◆ rold ⭈ bold
old
⳱ an
◆
◆ In the loop we have to distinguish two cases: i even and i odd. If i is even, the loop performs
◆ the following transformations:
◆
rnew ⳱ rold
◆
◆ bnew ⳱ b2old
◆
◆ inew ⳱ iold /2
◆ Therefore,
◆
◆ rnew ⭈ binew
new 2⭈i
⳱ rold ⭈ bold old
/2
◆
i
◆ ⳱ rold ⭈ bold
old
◆
◆ ⳱ an
◆
◆ On the other hand, if i is odd, then
◆
◆ rnew ⳱ rold ⭈ bold
◆
◆ bnew ⳱ bold
◆ inew ⳱ iold ⫺ 1
◆
258 Chapter 6. Iteration
◆ Therefore,
◆
◆ i ⫺1
rnew ⭈ binew
new
⳱ rold ⭈ bold + biold
old
◆
◆ i
⳱ rold ⭈ bold
old
◆
◆ ⳱ an
◆
◆ In either case, the new values for r, b, and i fulfill the loop invariant (I). So what? When the
◆ loop finally exits, (I) holds again:
◆
◆ r ⭈ bi ⳱ an
◆
◆ Furthermore, we know that i ⳱ 0, since the loop is terminating. But because i ⳱ 0, r ⭈ bi ⳱
◆ r ⭈ b0 ⳱r. Hence r ⳱ an , and the method really does compute the nth power of a.
◆ This technique is quite useful, because it can explain an algorithm that is not at all obvious.
◆ The condition (I) is called a loop invariant because it is true when the loop is entered, at the
◆ top of each pass, and when the loop is exited. If a loop invariant is chosen skillfully, you may
◆ be able to deduce correctness of a computation. See [4] for another nice example.
◆ any but the most trivial programs correct in such a way that the specification and the proof can
◆ be trusted more than the program. There is hope that correctness proofs will become more
◆ applicable to real-life programming situations in the future. At this point, however, engineer-
◆ ing and management are at least as important as mathematics and logic for the successful
◆ completion of large software projects.
Chapter Summary
2. There are three kinds of loops: while, for, and do loops. You use a for loop
when a number runs from a starting to an ending value with a constant increment or
decrement; do loops are appropriate when the loop body must be executed at least
once.
4. Loops can be nested. A typical example for nested loops is printing a table.
5. When reading input, you can detect the end of the input by checking for the end
of the file, or you can use a sentinel value.
6. You can break an input line into words by using a string tokenizer, or you can
access the individual characters.
7. In a simulation, you repeatedly generate random numbers and use them to simu-
late an activity.
8. With simple loops, you can use correctness proofs to show that the loop is correct.
Correctness proofs, as well as informal reasoning about correctness, work best if you
exit from a loop only when the loop condition fails. Therefore, it is best to avoid
breaking out from the middle of a loop.
Further Reading
[1] Alan Feuer, The C Puzzle Book, Prentice-Hall, 1989.
[2] E. W. Dijkstra, “Goto Statements Considered Harmful”, Communications of the ACM, vol.
11, no. 3 (March 1968), pp. 147–148.
260 Chapter 6. Iteration
Review Exercises
Exercise R6.1. Which loop statements does Java support? Give simple rules when
to use each loop type.
Exercise R6.3. How often do the following loops execute? Assume that i is not
changed in the loop body.
for (i = 1; i <= 10; i++) . . .
for (i = 0; i < 10; i++) . . .
for (i = 10; i > 0; i--) . . .
for (i = -10; i <= 10; i++) . . .
for (i = 10; i >= 0; i++) . . .
for (i = -10; i <= 10; i = i + 2) . . .
for (i = -10; i <= 10; i = i + 3) . . .
Review Exercises 261
Exercise R6.4. Rewrite the following for loop into a while loop.
int s = 0;
for (int i = 1; i <= 10; i++) s = s + i;
Exercise R6.6. What is an infinite loop? On your computer, how can you terminate
a program that executes an infinite loop?
Exercise R6.7. There are two ways to supply input to System.in. Describe both
methods. Explain how the “end of file” is signaled in both cases.
Exercise R6.8. In DOS/Windows and UNIX, there is no special “end of file” char-
acter stored in a file. Verify that statement by producing a file with known character
count—for example, a file consisting of the following three lines
Hello
cruel
world
Then look at the directory listing. How many characters does the file contain? Re-
member to count the newline characters. (In DOS, you may be surprised that the
count is not what you expect. DOS text files store each newline as a two-character
sequence. The input readers and output streams automatically translate between this
carriage return/line feed sequence used by files and the '\n' character used by Java
programs, so you don’t need to worry about it.) Why does this prove that there is no
“end of file” character? Why do you nevertheless need to type Ctrl+Z/Ctrl+D to end
console input?
Exercise R6.9. How can you read input from System.in (1) a character at a time,
(2) a word at a time, and (3) a line at a time?
Exercise R6.10. Show how to use a string tokenizer to break up the string "Hello,
cruel world!" into tokens. What are the resulting tokens?
Here the name of the bridge can be a single word (“Brooklyn”) or consist of several
words (“Golden Gate”). The length is a floating-point number.
Exercise R6.12. What is a “loop and a half”? Give three strategies to implement the
following “loop and a half”:
loop
{ read employee name
if not OK, exit loop
read employee salary
if not OK, exit loop
give employee 5 percent raise
print employee data
}
Use a Boolean variable, a break statement, and a method with multiple return
statements. Which of these three approaches do you find clearest?
Exercise R6.13. What is a sentinel value? Give simple rules when it is better to use
a sentinel value and when it is better to use the end of the input file to denote the
end of a data sequence. Hint: Consider the number of data sets and the origin of the
data (keyboard input vs. file input).
Exercise R6.14. How would you use a random number generator to simulate the
drawing of a playing card?
Exercise R6.15. What is an “off by one” error? Give an example from your own
programming experience.
Exercise R6.16. Give an example of a for loop in which symmetric bounds are
more natural. Give an example of a for loop in which asymmetric bounds are more
natural.
Exercise R6.17. What are nested loops? Give an example where a nested loop is
typically used.
Programming Exercises
Exercise P6.1. The series of pipes in Advanced Topic 6.5 has one final problem: The
output file contains upper- and lowercase versions of the same word, such as “The”
and “the”. Modify the procedure, either by changing one of the programs or, in the
true spirit of piping, by writing another short program and adding it to the series.
Exercise P6.2. Currency conversion. Write a program that asks the user to enter today’s
exchange rate between U.S. dollars and the Euro. Then the program reads U.S. dollar
Programming Exercises 263
values and converts each to Euro values. Use 0 as a sentinel to denote the end of
inputs.
Exercise P6.3. Write a program that asks the user to enter today’s exchange rate
between U.S. dollars and the Euro. Then the program reads U.S. dollar values and
converts each to Euro values. Use 0 as the sentinel value to denote the end of dollar
inputs. Then the program reads a sequence of Euro amounts and converts them to
dollars. The second sequence is terminated by the end of the input file.
Exercise P6.5. Projectile flight. Suppose a cannonball is propelled vertically into the
air with a starting velocity v0 . Any calculus book will tell us that the position of
the ball after t seconds is s(t) ⳱ ⫺0.5 ⭈ g ⭈ t 2 Ⳮ v0 ⭈ t, where g ⳱ 9.81 m/sec2 is
the gravitational force of the earth. No calculus book ever mentions why someone
would want to carry out such an obviously dangerous experiment, so we will do it
in the safety of the computer.
In fact, we will confirm the theorem from calculus by a simulation. In our simula-
tion, we will consider how the ball moves in very short time intervals ⌬t. In a short
time interval the velocity v is nearly constant, and we can compute the distance the
ball moves as ⌬s ⳱ v ⭈ ⌬t. In our program, we will simply set
double deltaT = 0.01;
In the next iteration the new velocity is used to update the distance.
Now run the simulation until the cannonball falls back onto the earth. Get the ini-
tial velocity as an input (100 m/sec is a good value). Update the position and velocity
100 times per second, but print out the position only every full second. Also print
out the values from the exact formula s(t) ⳱ ⫺0.5 ⭈ g ⭈ t 2 Ⳮ v0 ⭈ t for comparison.
264 Chapter 6. Iteration
What is the benefit of this kind of simulation when an exact formula is avail-
able? Well, the formula from the calculus book is not exact. Actually, the gravita-
tional force diminishes the further the cannonball is away from the surface of the
earth. This complicates the algebra sufficiently that it is not possible to give an exact
formula for the actual motion, but the computer simulation can simply be extended
to apply a variable gravitational force. For cannonballs, the calculus-book formula is
actually good enough, but computers are necessary to compute accurate trajectories
for higher-flying objects such as ballistic missiles.
Exercise P6.6. Most cannonballs are not shot upright but at an angle. If the starting
velocity has magnitude v and the starting angle is ␣ , then the velocity is actually a
vector with components vx ⳱ v cos ␣, vy ⳱ v sin ␣ . In the x-direction the velocity
does not change. In the y-direction the gravitational force takes its toll. Repeat the
simulation from the previous exercise, but store the position of the cannonball as a
Point2D variable. Update the x and y positions separately, and also update the x
and y components of the velocity separately. Every full second, plot the location of
the cannonball on the graphics display. Repeat until the cannonball has reached the
earth again.
This kind of problem is of historical interest. The first computers were designed to
carry out just such ballistic calculations, taking into account the diminishing gravity
for high-flying projectiles and wind speeds.
Exercise P6.7. The Fibonacci sequence is defined by the following rule. The first two
values in the sequence are 1 and 1. Every subsequent value is the sum of the two
values preceding it. For example, the third value is 1 Ⳮ 1 ⳱ 2, the fourth value is
1 Ⳮ 2 ⳱ 3, and the fifth is 2 Ⳮ 3 ⳱ 5. If fn denotes the nth value in the Fibonacci
sequence, then
f1 ⳱ 1
f2 ⳱ 1
fn ⳱ fn⫺1 Ⳮ fn⫺2 if n ⬎ 2
Write a program that prompts the user for n and prints the nth value in the Fibonacci
sequence.
Hint: There is no need to store all values for fn . You only need the last two values
to compute the next one in the series:
fold1 = 1;
fold2 = 1;
fnew = fold1 + fold2;
After that, discard fold2, which is no longer needed, and set fold2 to fold1 and
fold1 to fnew. Repeat computing fnew for an appropriate number of times.
Exercise P6.8. Write a program that prints a bar chart from a data set. The program
should be a graphics applet that prompts the user for the values, all to be entered
Programming Exercises 265
into a single option dialog, separated by spaces (for example, 40 60 50). Assume all
values are between 0 and 100. Then draw a bar chart like this:
Exercise P6.9. Mean and standard deviation. Write a program that reads a set of
floating-point data values from the input. When the end of file is reached, print out
the count of the values, the average, and the standard deviation. The average of a
data set 兵x1 , . . . , xn 其 is x ⳱ 冱 xi /n, where 冱 xi ⳱ x1 Ⳮ ⭈⭈⭈ Ⳮ xn is the sum of the
input values. The standard deviation is
冪 冱(xn ⫺⫺1x)
2
i
S ⳱
However, that formula is not suitable for our task. By the time you have computed
the mean, the individual xi are long gone. Until you know how to save these values,
use the numerically less stable formula
S ⳱ 冪 冱 xi2 ⫺ n1 冱 xi
n⫺1
You can compute this quantity by keeping track of the count, the sum, and the sum
of squares as you process the input values.
Exercise P6.10. Write a graphical applet that prompts a user to enter a number n
and that draws n circles with random center and random radius.
Exercise P6.11. Flesch Readability Index. The following index [6] was invented by
Flesch as a simple tool to gauge the legibility of a document without linguistic
analysis.
◆ Count all words in the file. A word is any sequence of characters delimited by
white space, whether or not it is an actual English word.
◆ Count all syllables in each word. To make this simple, use the following rules:
Each group of adjacent vowels (a,e,i,o,u,y) counts as one syllable (for example,
the “ea” in “real” contributes one syllable, but the “e..a” in “regal” count as two
syllables). However, an “e” at the end of a word doesn’t count as a syllable.
Also, each word has at least one syllable, even if the previous rules give a
count of 0.
266 Chapter 6. Iteration
This index is a number, usually between 0 and 100, indicating how difficult the text
is to read. Some examples for random material for various publications are
Comics 95
Consumer ads 82
Sports Illustrated 65
Time 57
New York Times 39
Auto insurance policy 10
Internal Revenue Code ⫺6
The purpose of the index is to force authors to rewrite their text until the index is
high enough. This is achieved by reducing the length of sentences and by removing
long words. For example, the sentence
The following index was invented by Flesch as a simple tool to estimate the legibility
of a document without linguistic analysis.
can be rewritten as
Flesch invented an index to check whether a text is easy to read. To compute the index,
you need not look at the meaning of the words.
His book [7] contains delightful examples of translating government regulations into
“plain English”.
Your program should read in a text file, compute the legibility index, and print out
the equivalent educational level.
Programming Exercises 267
Exercise P6.12. Factoring of integers. Write a program that asks the user for an integer
and then prints out all its factors. For example, when the user enters 150, the program
should print
2
3
5
5
Exercise P6.13. Prime numbers. Write a program that prompts the user for an integer
and then prints out all prime numbers up to that integer. For example, when the user
enters 20, the program should print
2
3
5
7
11
13
17
19
Recall that a number is a prime number if it is not divisible by any number except 1
and itself.
Exercise P6.14. The best known iterative method for computing the roots of a func-
tion f (that is, the x-values for which f (x) is 0) is Newton–Raphson approximation. To
find the zero of a function whose derivative is also known, compute
For this exercise, write a program to compute nth roots of floating-point numbers.
n
Prompt the user for a and n, then obtain 冪 a by computing a zero of the function
f (x) ⳱ x ⫺ a.
n
Exercise P6.16. Write a program that reads a series of floating-point numbers and
prints
Exercise P6.17. The game of Nim. This is a well-known game with a number of
variants. We will consider the following variant, which has an interesting winning
strategy. Two players alternately take marbles from a pile. In each move, a player
268 Chapter 6. Iteration
chooses how many marbles to take. The player must take at least one but at most
half of the marbles. Then the other player takes a turn. The player who takes the last
marble loses.
You will write a program in which the computer plays against a human opponent.
Generate a random integer between 10 and 100 to denote the initial size of the pile.
Generate a random integer between 0 and 1 to decide whether the computer or the
human takes the first turn. Generate a random integer between 0 and 1 to decide
whether the computer plays smart or stupid. In stupid mode, the computer simply
takes a random legal value (between 1 and n/2) from the pile whenever it has a turn.
In smart mode the computer takes off enough marbles to make the size of the pile
a power of two minus 1—that is, 3, 7, 15, 31, or 63. That is always a legal move,
except if the size of the pile is currently one less than a power of two. In that case,
the computer makes a random legal move.
You will note that the computer cannot be beaten in smart mode when it has the
first move, unless the pile size happens to be 15, 31, or 63. Of course, a human player
who has the first turn and knows the winning strategy can win against the computer.
Write a program that computes e x using this formula. Of course, you can’t compute
an infinite sum. Just keep adding values until an individual summand (term) is less
than a certain threshold. At each step, you need to compute the new term and add
it to the total. Update these terms as follows:
term = term * x / n;
Exercise P6.19. Program the following simulation: Darts are thrown at random
points onto the square with corners (1, 1) and (⫺1, ⫺1). If the dart lands inside the
unit circle (that is, the circle with center (0, 0) and radius 1), it is a hit. Otherwise it
is a miss. Run this simulation and use it to determine an approximate value for .
Explain why this is a better method for estimating than the Buffon needle program.
Exercise P6.20. It is easy and fun to draw graphs of curves with the Java graphics
library. Simply draw a hundred line segments joining the points (x, f (x)) and (x Ⳮ
d, f (x Ⳮ d )), where x ranges from xmin to xmax and d ⳱ (xmax ⫺ xmin )/100. Draw the
curve f (x) ⳱ x 3 / 100 ⫺ x Ⳮ 10, where x ranges from ⫺10 to 10 in this fashion.
Exercise P6.21. Draw a picture of the “four-leaved rose” whose equation in polar
coordinates is r ⳱ cos 2, 0 ⱕ ⱕ 2 . Let go from 0 to 2 in 100 steps. Each
time, compute r and then compute the (x, y) coordinates from the polar coordinates
by using the formula
x ⳱ r cos , y ⳱ r sin
You can get extra credit if you can vary the number of petals.