2.9.1 Adding Up a Sum
You now have every building block you need to make a program decide (if statements) and repeat (while and for loops). This topic is about putting those blocks together to solve problems that come up again and again: What is the total of these prices? What is the average score? How many days were hot? What was the coldest night? Programmers solve these so often that each has a well-known shape. A standard algorithm is a step-by-step solution to a common problem that programmers reuse, adjusting the details each time. Once you recognize the shape, you can read, trace, and fix code that uses it quickly, and you can combine shapes to solve new problems.
The first and most common problem is adding up a series of numbers. Think about how you add a stack of receipts by hand. You do not look at all of them at once. You start at zero, look at one receipt, add its amount to what you have so far, then move to the next receipt and add again. The number in your head is a running total: the sum of every value you have looked at so far.
A program does the same thing with a variable. An accumulator is a variable that collects a result a little at a time as a loop runs; when the result is a sum, the accumulator holds the running total. Every sum algorithm has the same three parts:
| Part | Code | Why it is there |
|---|---|---|
| Start the total at zero | int total = 0; |
Before you have looked at anything, the sum is zero. Done before loop. |
| Add one value on each pass | total += value; |
Each pass makes the running total include one more value. Done inside. |
| Use the total after the loop | println(total) |
Only when the loop has finished does the total include every value. |
The order matters. The total must be set to zero once, before the loop starts, so that the additions on later passes are not thrown away. The += from Topic 1.6 does the adding: total += value; means total = total + value;, so each pass builds on what the earlier passes stored.
Key idea
To add up a series of values, start an accumulator at 0 before the loop, add one value to it with += on each pass,
and use the finished total after the loop ends.
Worked example: Adding the numbers 1 through 5
Here the values to add come from the loop control variable itself, which counts 1, 2, 3, 4, 5. Watch total grow by the current value of number on each pass.
int total = 0;
for (int number = 1; number <= 5; number++)
{
total += number;
}
System.out.println(total);Line 1 runs once. Then the for loop from Topic 2.8 takes over: it sets number to 1, checks number <= 5, runs the body (line 4), updates number, and checks again.
| Pass | number |
total before line 4 |
total after line 4 |
|---|---|---|---|
| 1 | 1 | 0 | 1 |
| 2 | 2 | 1 | 3 |
| 3 | 3 | 3 | 6 |
| 4 | 4 | 6 | 10 |
| 5 | 5 | 10 | 15 |
After pass 5, number becomes 6, the condition 6 <= 5 is false, and the loop ends. Line 6 prints:
15Read the “before” column: each pass starts from exactly what the pass before it stored. That is why the total ends up as 1 + 2 + 3 + 4 + 5.
Now change where the values come from. Without lists of data (those come in Unit 4), a program usually gets a series of values by reading them one at a time. This example reads them with a Scanner. The first number in the input says how many prices follow, so a for loop can run exactly that many times. A keyboard Scanner would work the same way; this guide builds the Scanner from a string so the output is always the same.
Worked example: Totaling a receipt
import java.util.Scanner;
public class ReceiptTotal
{
public static void main(String[] args)
{
Scanner input = new Scanner("4 3.25 1.50 2.75 4.00");
int itemCount = input.nextInt();
double total = 0.0;
for (int item = 1; item <= itemCount; item++)
{
double price = input.nextDouble();
total += price;
}
System.out.print("Total: ");
System.out.println(total);
}
}Line 8 reads 4, the number of items. Line 9 starts the accumulator at 0.0; it is a double because prices have decimal parts. On each pass, line 12 reads the next price into price, and line 13 adds it to total.
item |
price read on line 12 |
total after line 13 |
|---|---|---|
| 1 | 3.25 | 3.25 |
| 2 | 1.5 | 4.75 |
| 3 | 2.75 | 7.5 |
| 4 | 4.0 | 11.5 |
When item becomes 5, the condition 5 <= 4 is false. Lines 15 and 16 print:
Total: 11.5Compare this with the first example. The loop and the accumulator have the same shape; only the source of each value changed, from the loop control variable to input.nextDouble(). Notice too that price is replaced on every pass, while total keeps growing. A variable that holds “the current value” and a variable that holds “the result so far” play very different roles.
Watch out
Resetting the total inside the loop. Students sometimes put the “start at zero” step inside the loop, thinking each pass needs a fresh start:
int total = 0;
for (int number = 1; number <= 5; number++)
{
total = 0;
total += number;
}
System.out.println(total);This prints 5, not 15. On every pass, line 4 wipes out the running total and line 5 adds only the current number. After the last pass, total holds just 0 + 5. Java gives no error, because setting a variable to zero is legal; the program simply answers a different question. The fix is to set the total to zero once, before the loop, as in the first worked example.
Pause and try
Question 1 · Tracing
What does this code print? Make a trace table with columns for k and total.
int total = 0;
for (int k = 3; k <= 12; k += 3)
{
total += k;
}
System.out.println(total);Check your reasoning
It prints 30.
k |
total after line 4 |
|---|---|
| 3 | 3 |
| 6 | 9 |
| 9 | 18 |
| 12 | 30 |
The update k += 3 makes k jump by threes, so the loop adds 3 + 6 + 9 + 12. After the pass with k equal to 12, k becomes 15, and 15 <= 12 is false, so the loop stops. If you got 18, you stopped before 12; the condition uses <=, so 12 itself is included.
Reread 2.9.1 Adding Up a Sum if your answer differed.
Question 2 · Debugging
This code is supposed to print the sum of the whole numbers from 1 through 10, which is 55. It prints 45 instead. Find the error and correct it.
int total = 0;
for (int number = 1; number < 10; number++)
{
total += number;
}
System.out.println(total);Check your reasoning
The condition on line 2 should be number <= 10:
for (int number = 1; number <= 10; number++)With number < 10, the loop stops as soon as number reaches 10, so 10 is never added: 1 + 2 + … + 9 is 45. This is an off-by-one error from Topic 2.7: the loop runs one time too few. With <=, the last pass adds 10 and the program prints 55. The accumulator part of the code was already correct; only the loop’s range was wrong.
Reread 2.9.1 Adding Up a Sum if your answer differed.
2.9.2 Computing an Average
A teacher does not usually want the total of a class’s quiz scores; she wants the average. A weather report gives the average rainfall, not the sum. To find an average (also called the mean), you add up the values and divide by how many values there are. So an average algorithm is the sum algorithm from 2.9.1 plus one division after the loop.
The division is where things go wrong, because of a rule from Topic 1.3: when both numbers are int, / throws away the decimal part. A sum of whole-number scores and a count of scores are both int, so sum / count can lose information. The fix, from Topic 1.5, is to cast one of them to double before dividing, so the division is done with decimals.
Key idea
An average is the sum divided by the number of values. When both are int, write (double) sum / count so the
division keeps its decimal part.
Worked example: Average rainfall over five days
The first input value is the number of days. Each pass of the loop reads one day’s rainfall and adds it straight into the accumulator. Line 12 passes the value from nextInt() directly to +=; no separate variable is needed when the value is used only once.
import java.util.Scanner;
public class RainfallAverage
{
public static void main(String[] args)
{
Scanner input = new Scanner("5 12 7 0 9 4");
int days = input.nextInt();
int totalRain = 0;
for (int day = 1; day <= days; day++)
{
totalRain += input.nextInt();
}
double average = (double) totalRain / days;
System.out.print("Average rainfall: ");
System.out.println(average);
}
}day |
Value read | totalRain after line 12 |
|---|---|---|
| 1 | 12 | 12 |
| 2 | 7 | 19 |
| 3 | 0 | 19 |
| 4 | 9 | 28 |
| 5 | 4 | 32 |
After the loop, line 14 computes the average. The cast applies only to totalRain, the value right after it, and it happens before the division. So Java works out 32.0 / 5, a double divided by an int, which is 6.4:
Average rainfall: 6.4Day 3 had no rain, but it still counts as a day. The value 0 adds nothing to the total, yet the division is still by 5.
Where the cast goes matters. This snippet tries three ways with the same total and count:
int totalRain = 32;
int days = 5;
System.out.println(totalRain / days);
System.out.println((double) (totalRain / days));
System.out.println((double) totalRain / days);6
6.0
6.4
| Expression | What Java does | Result |
|---|---|---|
totalRain / days |
int divided by int: decimal part dropped. |
6 |
(double) (totalRain / days) |
Parentheses first: 32 / 5 is 6; then cast. |
6.0 |
(double) totalRain / days |
Cast first: 32.0 / 5 is a double division. |
6.4 |
The middle one is the trap. It looks like it uses a cast, but by the time the cast happens, the integer division has already thrown away the .4. Casting 6 to a double gives 6.0, not 6.4.
So far the program knew how many values were coming. Often it does not: a teacher types scores until she is done. You met the usual way to handle this in Topic 2.7: a sentinel value, a special value that is not real data and that means “stop reading.” Quiz scores are never negative, so -1 works as a sentinel. Because the program does not know in advance how many values there will be, it uses a while loop (Topic 2.7) and a second accumulator. A counter is an accumulator that adds one each time something happens; here it adds one for each real value read.
Worked example: Average quiz score with a sentinel
import java.util.Scanner;
public class QuizAverage
{
public static void main(String[] args)
{
Scanner input = new Scanner("88 92 75 90 -1");
int sum = 0;
int count = 0;
int score = input.nextInt();
while (score != -1)
{
sum += score;
count++;
score = input.nextInt();
}
if (count > 0)
{
double average = (double) sum / count;
System.out.print("Average: ");
System.out.println(average);
}
else
{
System.out.println("No scores");
}
}
}The reading pattern has a special shape. Line 10 reads the first value before the loop, so the condition on line 11 has something to check. Inside the loop, line 15 reads the next value last, so the condition checks each new value before it is added.
| Step | score |
sum |
count |
|---|---|---|---|
| Line 10 reads | 88 | 0 | 0 |
| Pass 1 (lines 13–15) | 92 | 88 | 1 |
| Pass 2 | 75 | 180 | 2 |
| Pass 3 | 90 | 255 | 3 |
| Pass 4 | -1 | 345 | 4 |
The score column shows the value read at the end of each pass. After pass 4, score is -1, so score != -1 is false and the loop ends. The -1 is never added, which is the whole point of a sentinel. Line 19 computes 345.0 / 4:
Average: 86.25The if on line 17 guards the division. If the very first value were -1, the loop body would never run (Topic 2.7), count would still be 0, and there would be nothing to average. With input "-1" this program prints No scores instead.
Watch out
Dividing when there are no values. Without the count > 0 check, an empty input means dividing by zero. Here is a version that uses int division and no guard:
import java.util.Scanner;
public class AverageNoGuard
{
public static void main(String[] args)
{
Scanner input = new Scanner("-1");
int sum = 0;
int count = 0;
int score = input.nextInt();
while (score != -1)
{
sum += score;
count++;
score = input.nextInt();
}
int average = sum / count;
System.out.println(average);
}
}The loop never runs, so line 17 tries 0 / 0 and the program crashes:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at AverageNoGuard.main(AverageNoGuard.java:17)(The exact wording can vary between Java versions.) With the (double) cast it would instead be a double divided by zero, a case this course leaves out. Either way there is no meaningful average, so the fix is the same: check that count > 0 before dividing, as QuizAverage.java does.
Pause and try
Question 3 · Multiple choice
The int variables total and count hold 17 and 4. Which expression evaluates to 4.25?
total / count(double) (total / count)(double) total / count(int) ((double) total / count)
Check your reasoning
C. The cast turns total into 17.0 before the division, so Java divides a double by an int and keeps the decimal part: 17.0 / 4 is 4.25.
- A is wrong: both values are
int, so the division drops the decimal part and gives4. - B is wrong: the parentheses make
17 / 4happen first, which is already4; casting afterward gives4.0. - D is wrong: the inside correctly computes
4.25, but the outer(int)cast then chops off the decimal part, giving4.
Reread 2.9.2 Computing an Average if you chose differently.
Question 4 · Tracing
What does this program print? Trace score, sum, and count.
import java.util.Scanner;
public class SentinelZeroScore
{
public static void main(String[] args)
{
Scanner input = new Scanner("10 0 5 -1");
int sum = 0;
int count = 0;
int score = input.nextInt();
while (score != -1)
{
sum += score;
count++;
score = input.nextInt();
}
System.out.println((double) sum / count);
}
}Check your reasoning
It prints 5.0.
| Step | score |
sum |
count |
|---|---|---|---|
| Line 10 reads | 10 | 0 | 0 |
| Pass 1 | 0 | 10 | 1 |
| Pass 2 | 5 | 10 | 2 |
| Pass 3 | -1 | 15 | 3 |
The 0 is a real score, not the sentinel: the loop stops only at -1. So the 0 is counted, even though it adds nothing to the sum. Then (double) 15 / 3 is 5.0. If you got 7.5, you skipped the 0 and divided by 2.
Reread 2.9.2 Computing an Average if your answer differed.
2.9.3 Counting Values That Meet a Condition
In 2.9.2 you counted every value. Often you need to count only some of them. How many days this week were over 90 degrees? How many students scored in the B range? How many numbers from 1 to 100 are even? Each question asks for a frequency: the number of times a particular condition is true.
The plan is the counter from the average algorithm, which added one for every value, with an if statement wrapped around the count++. Start it at 0 before the loop. Inside the loop, test the current value; only when the condition is true, add one.
int count = 0;
// loop over the values:
if (condition about the current value)
{
count++;
}
// after the loop, count holds the answer
Why 0 and not 1? Before the loop has checked anything, no value has met the condition. Starting at 1 would claim one match before seeing any data.
Key idea
To count how often a condition is met, start a counter at 0 before the loop, and inside the loop add one only when
an if statement finds the condition true.
Worked example: Counting hot days
The input holds seven daily high temperatures. A day is “hot” when its high is above 90.
import java.util.Scanner;
public class HotDays
{
public static void main(String[] args)
{
Scanner input = new Scanner("7 88 93 91 79 95 90 84");
int days = input.nextInt();
int hotCount = 0;
for (int day = 1; day <= days; day++)
{
int temp = input.nextInt();
if (temp > 90)
{
hotCount++;
}
}
System.out.print("Hot days: ");
System.out.println(hotCount);
}
}day |
temp |
temp > 90? |
hotCount after the if |
|---|---|---|---|
| 1 | 88 | false | 0 |
| 2 | 93 | true | 1 |
| 3 | 91 | true | 2 |
| 4 | 79 | false | 2 |
| 5 | 95 | true | 3 |
| 6 | 90 | false | 3 |
| 7 | 84 | false | 3 |
Hot days: 3Look at day 6. A high of exactly 90 is not “above 90,” so 90 > 90 is false and the counter does not change. If the question had been “at least 90,” the condition would be temp >= 90 and the answer would be 4. The counting pattern never changes; the condition decides what is counted, so read it carefully at the boundary.
The condition can be anything you learned in Topics 2.2 through 2.6, including compound conditions. Here the question is how many scores fall from 70 to 89, including both ends.
Worked example: Counting scores in a range
import java.util.Scanner;
public class ScoreBand
{
public static void main(String[] args)
{
Scanner input = new Scanner("6 72 95 89 64 70 90");
int students = input.nextInt();
int inBand = 0;
for (int student = 1; student <= students; student++)
{
int score = input.nextInt();
if (score >= 70 && score <= 89)
{
inBand++;
}
}
System.out.print("Scores from 70 to 89: ");
System.out.println(inBand);
}
}A score is in the range only when it is at least 70 and at most 89, so line 13 joins two comparisons with && (Topic 2.5).
score |
score >= 70 |
score <= 89 |
Counted? |
|---|---|---|---|
| 72 | true | true | yes (1) |
| 95 | true | false | no |
| 89 | true | true | yes (2) |
| 64 | false | (skipped) | no |
| 70 | true | true | yes (3) |
| 90 | true | false | no |
Scores from 70 to 89: 3For 64, the first comparison is false, so && short-circuits and never checks the second (Topic 2.5); the whole condition is already false. Both 70 and 89 count because the comparisons use >= and <=.
Watch out
Leaving out the braces. As in Topic 2.3, without braces an if controls only the one statement right after it, no matter how the lines are indented. Here a student wanted to print each hot temperature and count it:
import java.util.Scanner;
public class HotDaysNoBraces
{
public static void main(String[] args)
{
Scanner input = new Scanner("7 88 93 91 79 95 90 84");
int days = input.nextInt();
int hotCount = 0;
for (int day = 1; day <= days; day++)
{
int temp = input.nextInt();
if (temp > 90)
System.out.println(temp);
hotCount++;
}
System.out.print("Hot days: ");
System.out.println(hotCount);
}
}93
91
95
Hot days: 7The prints are right, but the count is 7, the number of days, not the number of hot days. Only line 14 belongs to the if. Line 15 is indented to look as if it belongs, but Java ignores indentation, so hotCount++ runs on every pass. The fix is to put braces around both statements so that both are inside the if:
if (temp > 90)
{
System.out.println(temp);
hotCount++;
}Pause and try
Question 5 · Tracing
What two numbers does this program print?
import java.util.Scanner;
public class FreezeCount
{
public static void main(String[] args)
{
Scanner input = new Scanner("5 32 15 40 32 28");
int nights = input.nextInt();
int freezing = 0;
int aboveFreezing = 0;
for (int night = 1; night <= nights; night++)
{
int temp = input.nextInt();
if (temp <= 32)
{
freezing++;
}
else
{
aboveFreezing++;
}
}
System.out.println(freezing);
System.out.println(aboveFreezing);
}
}Check your reasoning
It prints 4 and then 1.
temp |
temp <= 32? |
freezing |
aboveFreezing |
|---|---|---|---|
| 32 | true | 1 | 0 |
| 15 | true | 2 | 0 |
| 40 | false | 2 | 1 |
| 32 | true | 3 | 1 |
| 28 | true | 4 | 1 |
The if-else adds one to exactly one of the two counters on every pass, so the two counts always add up to the number of nights, 5. Both 32s count as freezing because the condition is <=. If you got 2 and 3, you treated 32 as above freezing.
Reread 2.9.3 Counting Values That Meet a Condition if your answer differed.
Question 6 · Multiple choice
In ScoreBand.java, the scores were 72, 95, 89, 64, 70, and 90. Which condition on line 13 would count exactly the scores outside the range 70 to 89, giving a count of 3?
score < 70 && score > 89score < 70 || score > 89score <= 70 || score >= 89!(score > 70 && score < 89)
Check your reasoning
B. A score is outside the range when it is below 70 or above 89. That matches 95, 64, and 90, for a count of 3.
- A is wrong: no number can be below 70 and above 89 at the same time, so the condition is never true and the count is 0.
- C is wrong:
<=and>=also match the boundary scores 70 and 89, which are inside the range. The count is 5. - D is wrong for the same reason. By De Morgan’s law (Topic 2.6) it means
score <= 70 || score >= 89, the same as C, so it also counts 5.
Reread 2.9.3 Counting Values That Meet a Condition if you chose differently.
2.9.4 Testing Whether One Integer Divides Another
Can 84 marbles be shared equally among 7 friends with none left over? Is this year a multiple of 4? Is a number even? All of these ask whether one integer is evenly divisible by another: dividing leaves a remainder of zero.
You already have the tool. From Topic 1.3, a % b gives the remainder when a is divided by b. If the remainder is 0, then b goes into a exactly. So the test “is a evenly divisible by b?” is the Boolean expression
a % b == 0
and “is a not evenly divisible by b?” is a % b != 0. In this course, the number being divided is never negative and the number you divide by is always positive, so these are the only cases you need.
Key idea
a % b == 0 is true exactly when a is evenly divisible by b.Worked example: Sharing marbles
int marbles = 84;
int friends = 7;
System.out.println(marbles % friends);
if (marbles % friends == 0)
{
System.out.println("7 friends: shares evenly");
}
friends = 5;
System.out.println(marbles % friends);
if (marbles % friends == 0)
{
System.out.println("5 friends: shares evenly");
}
else
{
System.out.println("5 friends: some left over");
}0
7 friends: shares evenly
4
5 friends: some left overLine 3 prints 84 % 7. Seven goes into 84 exactly twelve times (7 × 12 = 84), so the remainder is 0 and the condition on line 4 is true. After line 8 changes friends to 5, line 9 prints 84 % 5: five goes into 84 sixteen times (80), leaving 4. The condition on line 10 is false, so the else runs.
The most common divisibility test is checking whether a number is even, which means “divisible by 2.” Every whole number leaves a remainder of either 0 or 1 when divided by 2:
for (int n = 6; n <= 9; n++)
{
System.out.print(n);
System.out.print(" % 2 is ");
System.out.println(n % 2);
}6 % 2 is 0
7 % 2 is 1
8 % 2 is 0
9 % 2 is 1
So n % 2 == 0 means n is even, and n % 2 != 0 means n is odd.
A divisibility test becomes much more powerful inside a loop. Combine it with the counting pattern from 2.9.3, and you can find every number that divides another.
Worked example: Finding the divisors of 12
A divisor of a number is a whole number that divides it evenly. This loop tries every candidate d from 1 up to the number itself and keeps the ones that leave no remainder.
int number = 12;
int divisorCount = 0;
for (int d = 1; d <= number; d++)
{
if (number % d == 0)
{
System.out.print(d);
System.out.print(" ");
divisorCount++;
}
}
System.out.println();
System.out.print("Divisors: ");
System.out.println(divisorCount);d |
12 % d |
Divisor? | divisorCount |
|---|---|---|---|
| 1 | 0 | yes | 1 |
| 2 | 0 | yes | 2 |
| 3 | 0 | yes | 3 |
| 4 | 0 | yes | 4 |
| 5 | 2 | no | 4 |
| 6 | 0 | yes | 5 |
| 7–11 | not 0 | no | 5 |
| 12 | 0 | yes | 6 |
Each divisor is printed with a space after it on one line, and line 12 then ends that line:
1 2 3 4 6 12
Divisors: 6Change only number to 13 and the same code prints 1 13 and Divisors: 2. A number greater than 1 whose only divisors are 1 and itself is called a prime number, so this one loop can also tell you whether a number is prime: it is prime exactly when the count is 2.
Watch out
Writing the two numbers in the wrong order. “84 is divisible by 7” puts 84 first, so the test is 84 % 7 == 0. It is easy to swap them:
int marbles = 84;
int friends = 7;
System.out.println(friends % marbles);
if (friends % marbles == 0)
{
System.out.println("Shares evenly");
}
else
{
System.out.println("Some left over");
}7
Some left overfriends % marbles is 7 % 84. Since 84 does not go into 7 even once, the whole 7 is left over, so the remainder is 7 and the test fails. Read a % b == 0 as “a divided by b leaves nothing,” with the bigger whole (the thing being shared) on the left. The fix is marbles % friends == 0.
Pause and try
Question 7 · Debugging
This code is supposed to count the even numbers from 1 to 10, which should be 5. It prints 1. Find the error and correct it.
int evenCount = 0;
for (int n = 1; n <= 10; n++)
{
if (n / 2 == 0)
{
evenCount++;
}
}
System.out.println(evenCount);Check your reasoning
Line 4 uses / where it needs %. The corrected line is:
if (n % 2 == 0)n / 2 is the integer quotient, not the remainder. It is 0 only when n is 1 (because 1 / 2 is 0 in integer division), so the counter goes up only once. n % 2 is the remainder, which is 0 exactly for 2, 4, 6, 8, and 10. With the fix the program prints 5.
Reread 2.9.4 Testing Whether One Integer Divides Another if your answer differed.
Question 8 · Multiple choice
What does this code print?
for (int n = 1; n <= 30; n++)
{
if (n % 4 == 0 && n % 6 == 0)
{
System.out.println(n);
}
}12and24, each on its own line4,6,8,12,16,18,20,24,28, and30, each on its own line24only- Nothing
Check your reasoning
A. With &&, a number is printed only when it is divisible by 4 and by 6. From 1 to 30, the multiples of 4 are 4, 8, 12, 16, 20, 24, 28, and the multiples of 6 are 6, 12, 18, 24, 30. The numbers in both lists are 12 and 24.
- B is wrong: that list is every number divisible by 4 or 6, which is what
||would print. - C is wrong: 24 = 4 × 6, but 12 is also divisible by both (12 = 4 × 3 = 6 × 2). Do not assume the first common multiple is the product.
- D is wrong: the loop reaches 12 and 24, and both pass the test.
Reread 2.9.4 Testing Whether One Integer Divides Another if you chose differently.
2.9.5 Pulling Apart the Digits of an Integer
A credit card number has a check digit that catches typos. A puzzle asks for the sum of the digits of a number. A program reports how many digits a number has. Each needs the individual digits of an int, but an int is a single number, not a row of separate digits. How do you get at them?
The answer uses the two kinds of integer division from Topic 1.3, with 10:
number % 10is the last digit (the ones digit). Dividing by 10 leaves the ones digit as the remainder:4072 % 10is2.number / 10drops the last digit. Integer division by 10 throws away the ones digit:4072 / 10is407.
Why 10? Our numbers are written in base ten: each place is worth ten times the place to its right. Dividing by 10 shifts every digit one place to the right, and whatever falls off the end is the remainder. You can combine the two to reach any digit:
int number = 4072;
System.out.println(number % 10);
System.out.println(number / 10);
System.out.println(number / 10 % 10);
System.out.println(number / 100 % 10);| Line | Expression | Work | Result |
|---|---|---|---|
| 2 | number % 10 |
ones digit of 4072 | 2 |
| 3 | number / 10 |
drop the 2 | 407 |
| 4 | number / 10 % 10 |
407 % 10 (tens digit) |
7 |
| 5 | number / 100 % 10 |
40 % 10 (hundreds) |
0 |
Line 4 has no parentheses but still works left to right: / and % have the same precedence, so number / 10 happens first and % 10 is applied to its result.
To visit every digit without knowing how many there are, put the two steps in a while loop: take the last digit, then drop it, and repeat until nothing is left. When the number reaches 0, every digit has been removed.
Key idea
number % 10 gives the last digit and number / 10 removes it. Repeating both in a while (number > 0) loop visits
every digit, from right to left.
Worked example: Printing the digits of 4072
int number = 4072;
while (number > 0)
{
int digit = number % 10;
System.out.println(digit);
number = number / 10;
}| Pass | number at start |
digit (line 4) |
number after line 6 |
|---|---|---|---|
| 1 | 4072 | 2 | 407 |
| 2 | 407 | 7 | 40 |
| 3 | 40 | 0 | 4 |
| 4 | 4 | 4 | 0 |
2
7
0
4After pass 4, number is 0, so 0 > 0 is false and the loop ends. The digits come out right to left, because % 10 always takes the rightmost one. Notice that the zero in the middle is found like any other digit. Line 6 is what moves the loop toward ending; if it were missing, number would stay 4072 forever and number > 0 would always be true, an infinite loop (Topic 2.7).
Because the loop destroys the number as it goes, a program that still needs the original value copies it first. This next example does that and uses two accumulators at once: a sum of the digits and a count of the digits.
Worked example: Adding and counting the digits of 9035
int number = 9035;
int remaining = number;
int digitSum = 0;
int digitCount = 0;
while (remaining > 0)
{
digitSum += remaining % 10;
digitCount++;
remaining /= 10;
}
System.out.print(number);
System.out.print(" has ");
System.out.print(digitCount);
System.out.print(" digits that add up to ");
System.out.println(digitSum);remaining at start |
remaining % 10 |
digitSum |
digitCount |
|---|---|---|---|
| 9035 | 5 | 5 | 1 |
| 903 | 3 | 8 | 2 |
| 90 | 0 | 8 | 3 |
| 9 | 9 | 17 | 4 |
9035 has 4 digits that add up to 17The loop changes only remaining, so number still holds 9035 when lines 11 to 15 print it. Line 9 uses /= from Topic 1.6; remaining /= 10; means the same as remaining = remaining / 10;.
These digit algorithms assume the number is not negative. That is the usual situation (counts, years, ID numbers), and the remainder operator with negative numbers is outside this course.
Watch out
Forgetting the number 0. How many digits does 0 have? One. But the digit-counting loop says zero:
int number = 0;
int digitCount = 0;
while (number > 0)
{
digitCount++;
number /= 10;
}
System.out.println(digitCount);This prints 0. The condition 0 > 0 is false the very first time, so the body never runs (Topic 2.7). The loop is correct for every positive number but misses this edge case. One fix is to handle 0 separately with an if before the loop:
int number = 0;
int digitCount = 0;
if (number == 0)
{
digitCount = 1;
}
while (number > 0)
{
digitCount++;
number /= 10;
}
System.out.println(digitCount);This prints 1. For any positive number, the if is skipped and the loop counts as before.
Pause and try
Question 9 · Tracing
What does this code print?
int n = 615;
while (n > 0)
{
System.out.print(n % 10);
n /= 10;
}
System.out.println();Check your reasoning
It prints 516 on one line.
n at start |
n % 10 printed |
n after line 5 |
|---|---|---|
| 615 | 5 | 61 |
| 61 | 1 | 6 |
| 6 | 6 | 0 |
print keeps everything on one line, and the digits come out right to left, so the number appears reversed. Line 7 then ends the line. If you got 615, you printed the digits left to right; % 10 always gives the rightmost digit first.
Reread 2.9.5 Pulling Apart the Digits of an Integer if your answer differed.
Question 10 · Multiple choice
The int variable n holds 4729. Which expression evaluates to the hundreds digit, 7?
n % 100n / 100n / 100 % 10n % 10 / 100
Check your reasoning
C. n / 100 drops the last two digits, giving 47; then % 10 takes the last digit of that, 7.
- A is wrong:
% 100keeps the last two digits,29. - B is wrong: it gives
47, which still has the thousands digit in front. - D is wrong:
n % 10is9, and9 / 100is0in integer division.
Reread 2.9.5 Pulling Apart the Digits of an Integer if you chose differently.
2.9.6 Finding the Minimum or Maximum
Which player had the highest score? What was the coldest night of the week? These ask for the maximum (largest value) or the minimum (smallest value) in a series.
Think about how you would find the tallest person in a line if you could see only one person at a time. You would remember the tallest person so far. You look at the first person: so far, they are the tallest. For each next person, you compare: if this one is taller than the one you remember, you remember this one instead. When you reach the end, the person you remember is the tallest of all.
A program does exactly that with a variable that holds the best so far. Two decisions make up the algorithm:
- Where to start. Before the loop, the best-so-far variable needs a starting value. The safest choice is the first value itself, since it is a real piece of data. Another choice is a value that every real value is guaranteed to beat:
Integer.MAX_VALUEwhen looking for a minimum (everyintis at most that), orInteger.MIN_VALUEwhen looking for a maximum. - How to update. Inside the loop, compare the current value with the best so far. For a maximum,
if (value > best); for a minimum,if (value < best). Only when the comparison is true, replace the best so far.
Key idea
To find a maximum or minimum, keep a “best so far” variable. Start it at the first value (or at a value every real value will beat), and inside the loop replace it whenever the current value is larger (for a maximum) or smaller (for a minimum).
Worked example: The highest score
import java.util.Scanner;
public class HighestScore
{
public static void main(String[] args)
{
Scanner input = new Scanner("5 71 88 64 93 80");
int players = input.nextInt();
int highest = input.nextInt();
for (int player = 2; player <= players; player++)
{
int score = input.nextInt();
if (score > highest)
{
highest = score;
}
}
System.out.print("Highest: ");
System.out.println(highest);
}
}Line 9 reads the first score straight into highest. Because the first value has already been used, the loop on line 10 starts player at 2 and runs for the remaining four players.
player |
score |
score > highest? |
highest after the if |
|---|---|---|---|
| (line 9) | 71 | 71 | |
| 2 | 88 | true | 88 |
| 3 | 64 | false | 88 |
| 4 | 93 | true | 93 |
| 5 | 80 | false | 93 |
Highest: 93If the loop had started player at 1, it would try to read six scores when only five remain, an off-by-one error. Starting at 2 matches the fact that one score was read before the loop.
Now vary three things: look for the minimum, start at Integer.MAX_VALUE instead of the first value, and also remember where the minimum happened. Recording the position is a small addition: whenever the best so far changes, store the day too.
Worked example: The coldest day and when it happened
import java.util.Scanner;
public class ColdestDay
{
public static void main(String[] args)
{
Scanner input = new Scanner("5 -3 4 -8 2 -5");
int days = input.nextInt();
int coldest = Integer.MAX_VALUE;
int coldestDay = 0;
for (int day = 1; day <= days; day++)
{
int temp = input.nextInt();
if (temp < coldest)
{
coldest = temp;
coldestDay = day;
}
}
System.out.print("Coldest: ");
System.out.print(coldest);
System.out.print(" on day ");
System.out.println(coldestDay);
}
}day |
temp |
temp < coldest? |
coldest, coldestDay |
|---|---|---|---|
| start | Integer.MAX_VALUE, 0 |
||
| 1 | -3 | true | -3, 1 |
| 2 | 4 | false | -3, 1 |
| 3 | -8 | true | -8, 3 |
| 4 | 2 | false | -8, 3 |
| 5 | -5 | false | -8, 3 |
Coldest: -8 on day 3On day 1, -3 < 2147483647 is certainly true, so the first real temperature always replaces the starting value. That is why Integer.MAX_VALUE is safe for a minimum: no int is larger. This version reads every value inside the loop, so day starts at 1. Lines 16 and 17 are both inside the braces, so the day is recorded only when the temperature is a new low.
Watch out
Starting the best so far at 0. Zero feels like a natural starting point, the way it is for a sum, but here it can be wrong. Look at a cold week where every high temperature is below zero:
import java.util.Scanner;
public class MaxStartZero
{
public static void main(String[] args)
{
Scanner input = new Scanner("4 -6 -2 -9 -4");
int days = input.nextInt();
int warmest = 0;
for (int day = 1; day <= days; day++)
{
int temp = input.nextInt();
if (temp > warmest)
{
warmest = temp;
}
}
System.out.println(warmest);
}
}This prints 0, but no day had a high of 0. Every temperature (-6, -2, -9, -4) is less than 0, so temp > warmest is never true and the made-up starting value is never replaced. Starting at 0 works only if you know some value will beat it. The fix is to start with the first real value, as HighestScore.java does:
import java.util.Scanner;
public class MaxStartFirst
{
public static void main(String[] args)
{
Scanner input = new Scanner("4 -6 -2 -9 -4");
int days = input.nextInt();
int warmest = input.nextInt();
for (int day = 2; day <= days; day++)
{
int temp = input.nextInt();
if (temp > warmest)
{
warmest = temp;
}
}
System.out.println(warmest);
}
}This prints -2, the warmest of the four.
Pause and try
Question 11 · Tracing
What does this program print?
import java.util.Scanner;
public class MinUpdates
{
public static void main(String[] args)
{
Scanner input = new Scanner("4 12 9 15 9");
int count = input.nextInt();
int lowest = input.nextInt();
int updates = 0;
for (int i = 2; i <= count; i++)
{
int value = input.nextInt();
if (value < lowest)
{
lowest = value;
updates++;
}
}
System.out.print("Lowest: ");
System.out.println(lowest);
System.out.print("Updates: ");
System.out.println(updates);
}
}Check your reasoning
It prints:
Lowest: 9
Updates: 1i |
value |
value < lowest? |
lowest, updates |
|---|---|---|---|
| (line 9) | 12 | 12, 0 | |
| 2 | 9 | true | 9, 1 |
| 3 | 15 | false | 9, 1 |
| 4 | 9 | false | 9, 1 |
The second 9 does not count as an update, because 9 < 9 is false: a tie is not “smaller.” If you got Updates: 2, you treated < as if it were <=.
Reread 2.9.6 Finding the Minimum or Maximum if your answer differed.
Question 12 · Short answer
ColdestDay.java started coldest at Integer.MAX_VALUE. Would the program still work if it started coldest at 0 instead? Explain, using a week of summer temperatures such as 18, 25, and 21 as an example.
Check your reasoning
Model answer: No, not for every input. It happens to work for the given data because some temperatures are below 0. But in a week like 18, 25, and 21, every temperature is greater than 0, so temp < coldest is never true and the program reports 0, a temperature that never happened. Integer.MAX_VALUE works because every real temperature is less than or equal to it, so the first temperature always replaces it.
A complete answer says:
- The starting value must be one that real data will replace (or a real data value itself).
- With a start of
0, positive-only data never beats it, so the minimum comes out as0.
Reread 2.9.6 Finding the Minimum or Maximum if your reasoning differed.
Putting it together
Real programs rarely use just one of these algorithms. They run several in the same loop, each with its own variable. This program reads a week of step counts from a fitness tracker and reports the total, the average, how many days met a 10,000-step goal, and the best day. Comments (Topic 1.8) label the job of each variable.
Worked example: A weekly step report
import java.util.Scanner;
public class StepReport
{
public static void main(String[] args)
{
Scanner input = new Scanner("7 8200 10450 6100 12000 9800 10450 6000");
int days = input.nextInt();
int total = 0; // sum of all step counts
int goalDays = 0; // days with at least 10000 steps
int best = 0; // most steps in one day so far
int bestDay = 0; // the day that happened
for (int day = 1; day <= days; day++)
{
int steps = input.nextInt();
total += steps;
if (steps >= 10000)
{
goalDays++;
}
if (day == 1 || steps > best)
{
best = steps;
bestDay = day;
}
}
System.out.print("Total: ");
System.out.println(total);
System.out.print("Average: ");
System.out.println((double) total / days);
System.out.print("Goal days: ");
System.out.println(goalDays);
System.out.print("Best: ");
System.out.print(best);
System.out.print(" on day ");
System.out.println(bestDay);
}
}Four algorithms share one loop:
| Algorithm | Variable(s) | Lines |
|---|---|---|
| Sum | total |
10, 18 |
| Count with a test | goalDays |
11, 19–22 |
| Maximum (and when) | best, bestDay |
12–13, 23–27 |
| Average | total and days |
33, after loop |
Line 23 is a new twist on the maximum. Instead of reading the first value before the loop, it says “replace the best if this is day 1 or the steps beat the best so far.” On day 1 the left side is true, so || short-circuits (Topic 2.5) and the first value always becomes the best. After that, day == 1 is false and only steps > best matters.
day |
steps |
total |
goalDays |
best, bestDay |
|---|---|---|---|---|
| 1 | 8200 | 8200 | 0 | 8200, 1 |
| 2 | 10450 | 18650 | 1 | 10450, 2 |
| 3 | 6100 | 24750 | 1 | 10450, 2 |
| 4 | 12000 | 36750 | 2 | 12000, 4 |
| 5 | 9800 | 46550 | 2 | 12000, 4 |
| 6 | 10450 | 57000 | 3 | 12000, 4 |
| 7 | 6000 | 63000 | 3 | 12000, 4 |
After the loop, line 33 computes (double) 63000 / 7, which is 9000.0:
Total: 63000
Average: 9000.0
Goal days: 3
Best: 12000 on day 4Day 5 (9800) is close to the goal but is not counted, because 9800 >= 10000 is false. Each variable did its one job on every pass, and none of them interfered with the others.
The algorithms also combine in less obvious ways. There is an old shortcut for checking whether a number is divisible by 9: add up its digits, and if that sum is divisible by 9, so is the number. This snippet uses the digit algorithm to build a sum, then two divisibility tests to compare the shortcut with the direct check.
Worked example: The divisible-by-9 shortcut
int number = 7848;
int remaining = number;
int digitSum = 0;
while (remaining > 0)
{
digitSum += remaining % 10;
remaining /= 10;
}
System.out.print("Digit sum: ");
System.out.println(digitSum);
if (digitSum % 9 == 0)
{
System.out.println("Digit sum is a multiple of 9");
}
if (number % 9 == 0)
{
System.out.println("Number is a multiple of 9");
}The loop visits 8, 4, 8, 7, so digitSum is 8 + 4 + 8 + 7 = 27. Then 27 % 9 is 0, and 7848 % 9 is also 0 (7848 = 9 × 872), so both ifs run:
Digit sum: 27
Digit sum is a multiple of 9
Number is a multiple of 9Change number to 7849 and the digit sum becomes 28. Both 28 % 9 and 7849 % 9 are 1, so neither message prints. The shortcut and the direct test agree, as they always do for 9. A program like this is an original algorithm: no single standard pattern solves it, but it is built entirely from the ones in this chapter.
Vocabulary
| Term | Definition | Example |
|---|---|---|
| standard algorithm | A well-known step-by-step solution to a common problem that programmers reuse and adapt | Summing values in a loop |
| running total | The sum of every value looked at so far | total after pass 3 |
| accumulator | A variable that collects a result a little at a time as a loop runs | total += price; |
| average | The sum of the values divided by how many values there are | (double) sum / count |
| sentinel value | A special value that is not real data and signals the end of the input | -1 after quiz scores |
| frequency | The number of times a condition is true | 3 hot days |
| counter | An accumulator that adds one each time something happens | hotCount++; |
| evenly divisible | Divides with a remainder of zero | 84 % 7 == 0 |
| divisor | A whole number that divides another evenly | 4 is a divisor of 12 |
| digit | One of the single figures 0–9 that make up a written number; % 10 gives the last one |
4072 % 10 is 2 |
| maximum | The largest value in a series | 93 in 71, 88, 64, 93, 80 |
| minimum | The smallest value in a series | -8 in -3, 4, -8, 2, -5 |
| best so far | The variable in a minimum or maximum algorithm holding the smallest or largest value seen so far | highest in HighestScore |
Chapter summary
- A sum uses an accumulator that starts at
0before the loop, gains one value per pass with+=, and is used after the loop. Resetting it inside the loop throws away the running total. - An average is the sum divided by the count. Write
(double) sum / countso the division keeps its decimal part, and check that the count is greater than zero first. A sentinel value lets a while loop read an unknown number of values. - Counting how often a condition is met puts
count++inside an if inside the loop. The condition, including its boundary (>versus>=), decides what is counted. a % b == 0tests whetherais evenly divisible byb;n % 2 == 0tests for an even number. Inside a loop, it can find every divisor of a number.% 10gives the last digit of a nonnegative integer and/ 10removes it; awhile (number > 0)loop repeating both visits every digit from right to left.- A minimum or maximum keeps a best-so-far variable that starts at the first value (or a value every real value will beat) and is replaced whenever the current value is smaller or larger.
Review questions
Question 13 · Multiple choice
What does this code print?
int sum = 0;
int count = 0;
for (int k = 1; k <= 10; k++)
{
if (k % 3 == 0)
{
sum += k;
}
else
{
count++;
}
}
System.out.println(sum);
System.out.println(count);18and then718and then1055and then718and then3
Check your reasoning
A. The loop runs for k from 1 to 10. When k is divisible by 3 (that is, 3, 6, and 9), the if adds k to sum, giving 3 + 6 + 9 = 18. On every other pass, the else adds one to count. There are 10 passes and 3 of them went to the if, so count is 7.
- B is wrong: it counts every pass, but the else runs only when the if does not.
- C is wrong: 55 is the sum of all of 1 to 10. Only the multiples of 3 are added.
- D is wrong: 3 is how many numbers went to the if;
countis changed only in the else.
Reread 2.9.3 Counting Values That Meet a Condition and 2.9.4 Testing Whether One Integer Divides Another if you chose differently.
Question 14 · Debugging
This code is supposed to count the numbers from 1 to 50 that are divisible by both 2 and 3. The correct answer is 8 (6, 12, 18, 24, 30, 36, 42, 48), but it prints 33. Find the error and correct it.
int count = 0;
for (int n = 1; n <= 50; n++)
{
if (n % 2 == 0 || n % 3 == 0)
{
count++;
}
}
System.out.println(count);Check your reasoning
Line 4 uses || where it needs &&:
if (n % 2 == 0 && n % 3 == 0)With ||, a number is counted when it is divisible by 2 or by 3, which matches 33 of the numbers from 1 to 50 (25 even numbers, plus the 8 odd multiples of 3: 3, 9, 15, …, 45). “Both” means both tests must be true, which is &&. With the fix, only the multiples of 6 pass, and the program prints 8. The loop and the counter were already correct; only the condition was wrong, which is why the condition is the first place to look when a count is too high or too low.
Reread 2.9.4 Testing Whether One Integer Divides Another if your answer differed.
Question 15 · Tracing
What does this code print? Trace number and zeros.
int number = 70300;
int zeros = 0;
while (number > 0)
{
if (number % 10 == 0)
{
zeros++;
}
number /= 10;
}
System.out.print("Zeros: ");
System.out.println(zeros);Check your reasoning
It prints Zeros: 3.
number at start |
number % 10 |
zeros |
|---|---|---|
| 70300 | 0 | 1 |
| 7030 | 0 | 2 |
| 703 | 3 | 2 |
| 70 | 0 | 3 |
| 7 | 7 | 3 |
After the last pass, number is 0 and the loop ends. This combines the digit loop with the counting pattern: each digit is tested with an if, and only zeros are counted. The zeros at the right end are found too; each one is the last digit at some point before /= 10 removes it.
Reread 2.9.5 Pulling Apart the Digits of an Integer if your answer differed.
Question 16 · Multiple choice
A store’s daily sales are read by this program. Sales of 55 happened on both day 2 and day 4. What does it print?
import java.util.Scanner;
public class TieLastDay
{
public static void main(String[] args)
{
Scanner input = new Scanner("5 40 55 30 55 20");
int days = input.nextInt();
int best = input.nextInt();
int bestDay = 1;
for (int day = 2; day <= days; day++)
{
int sales = input.nextInt();
if (sales >= best)
{
best = sales;
bestDay = day;
}
}
System.out.print("Best day: ");
System.out.println(bestDay);
}
}Best day: 2Best day: 4Best day: 55Best day: 5
Check your reasoning
B. The comparison on line 14 is >=, so a value that ties the best so far also replaces it.
day |
sales |
sales >= best? |
best, bestDay |
|---|---|---|---|
| (line 9) | 40 | 40, 1 | |
| 2 | 55 | true | 55, 2 |
| 3 | 30 | false | 55, 2 |
| 4 | 55 | true (tie) | 55, 4 |
| 5 | 20 | false | 55, 4 |
- A is wrong: day 2 is what the program would print with
>, which keeps the first of tied values. With>=, the last tie wins. - C is wrong: 55 is the best sales amount, stored in
best; the program printsbestDay. - D is wrong: day 5 has the lowest sales, and
20 >= 55is false.
Reread 2.9.6 Finding the Minimum or Maximum if you chose differently.
Question 17 · Short answer
A program reads quiz scores until it reaches a sentinel, then prints their average. A student suggests using 0 as the sentinel instead of -1. Explain why that is a bad choice, and say what makes a good sentinel.
Check your reasoning
Model answer: 0 is a real quiz score: a student who missed every question gets a 0. If 0 were the sentinel, the loop would stop at the first real 0 score, leaving it and every score after it out of both the sum and the count, so the average would be wrong. A good sentinel is a value that can never be real data, so reading it can only mean “stop.” Scores are never negative, so -1 can never be mistaken for a score.
A complete answer says:
0can be real data, so a real score would end the input early.- The scores after it (and the 0 itself) would be missing from the sum and count.
- A sentinel must be a value outside the range of possible real values.
Reread 2.9.2 Computing an Average if your reasoning differed.
Question 18 · Tracing
What three lines does this code print? (Starting largest at 0 is safe here: why?)
int number = 2468;
int sum = 0;
int largest = 0;
while (number > 0)
{
int digit = number % 10;
sum += digit;
if (digit > largest)
{
largest = digit;
}
number /= 10;
}
System.out.println(sum);
System.out.println(largest);
System.out.println(sum % largest == 0);Check your reasoning
It prints:
20
8
falsenumber at start |
digit |
sum |
largest |
|---|---|---|---|
| 2468 | 8 | 8 | 8 |
| 246 | 6 | 14 | 8 |
| 24 | 4 | 18 | 8 |
| 2 | 2 | 20 | 8 |
The loop runs a sum and a maximum together over the digits. The last line prints the value of the Boolean expression sum % largest == 0: 20 % 8 is 4, not 0, so it is false (20 is not divisible by 8).
Starting largest at 0 is safe because a digit is never less than 0, so the first digit is always at least as big as the start, and any positive digit replaces it. This is different from the cold-week temperatures in 2.9.6, where every real value was below 0. (The program assumes number is positive, so at least one digit is visited.)
Reread 2.9.5 Pulling Apart the Digits of an Integer and 2.9.6 Finding the Minimum or Maximum if your answer differed.
Reflection
- I can trace a sum with an accumulator and explain why it starts at 0 before the loop. Reread
- I can compute an average without losing the decimal part, and explain what a sentinel value is for. Reread
- I can trace a loop that counts how many values meet a condition, including at the boundary. Reread
- I can use % to test whether one integer is evenly divisible by another. Reread
- I can use % 10 and / 10 in a loop to visit every digit of an integer. Reread
- I can find a minimum or maximum and explain how the starting value can change the answer. Reread
- I can trace a program that runs several of these algorithms in the same loop. Reread