Cambridge AS & A Level · Part 2 · Chapter 12
Algorithm Design and Problem-Solving
How to turn a problem into steps a computer can follow: abstraction, decomposition, pseudocode, flowcharts, loops and modules. Every worked example on this page runs in the step-through pseudocode lab.
Guru Jazzy🎯 Learning objectives
By the end of this chapter you should be able to:
- show an understanding of abstraction and describe its purpose
- produce an abstract model of a system by including only the essential details
- Describe and use decomposition
- break problems down into sub-problems, leading to the idea of a program module
- show that an algorithm is a solution expressed as a sequence of defined steps
- choose suitable identifier names and record them in an identifier table
- write pseudocode containing input, process and output
- write pseudocode using the four basic constructs: assignment, sequence, selection and repetition
- document a simple algorithm using pseudocode
- write pseudocode from a structured English description or a flowchart
- use stepwise refinement until a task is detailed enough to program
- use logic statements to define parts of an algorithm
🧠 12.01 What is computational thinking?
Computational thinking is a problem-solving process: you take a number of logical steps to analyse a problem and reach a solution that people can understand and computers can carry out. It has five strands:
Algorithm (key term): a sequence of defined steps that can be carried out to perform a task.
Abstraction in everyday life
The London Underground map (Figure 12.02) is a famous abstraction. It doesn't show where the tracks really run, or the streets above. It shows only the stations and which lines connect them, because that is all you need to plan a journey. Everything else has been filtered out.
Abstraction is what lets us cope with complexity. An algorithm is itself an abstraction of a process (inputs → steps → outputs), and an abstract data type defines a set of values and the operations on them, without saying how they are stored.
What a good answer includes
- Keep: A and B, the roads and footpaths you could walk along, the bridges over the canal, the railway line (as a barrier, and as a landmark), and junctions where you turn.
- Leave out: trees, cars, roof colours, the exact shapes of buildings, the sports pitch and solar panels.
- Straight lines and simple shapes are fine. Accurate distances and angles don't matter; which paths connect to which does.
- Test it: could a friend who has never been there follow your map from A to B?
📋 12.02 What is an algorithm?
We follow algorithms every day. Changing a car wheel, from a manual:
- Take a spanner and loosen the wheel nuts.
- Position a jack in an appropriate place.
- Raise the car.
- Take off the wheel nuts and the wheel.
- Lift the replacement wheel into position.
- Replace the wheel nuts and tighten by hand.
- Lower the car.
- Fully tighten the wheel nuts.
The order matters. Do Step 1 after Step 3 and the wheel just spins in the air, so you can't loosen the nuts. You can't do Step 4 before Step 3 either.
🎮 Put the wheel-change steps in order
Use ▲ ▼ to move the steps, then check.
A recipe is an algorithm
- Measure 200 g sugar, 200 g butter, 4 eggs, 200 g flour, 2 teaspoons baking powder and 2 tablespoons of milk.
- Mix the ingredients together in a large bowl until the mixture is smooth.
- Pour the mixture into a cake tin.
- Bake in the oven at 190 °C for 20 minutes.
- Check it is fully cooked.
- Turn the cake out of the tin and cool on a wire rack.
The ingredients are the input, the cake is the output, and mixing and baking is the process.
Some steps need breaking down further. Step 2 becomes:
- 2.1 Beat the sugar and butter together until fluffy.
- 2.2 Add the eggs, one at a time, mixing constantly.
- 2.3 Sieve the flour and baking powder and stir slowly into the egg mixture.
- 2.4 Add milk and mix to give a creamy consistency.
🚇 Steps that depend on a condition
From King's Cross St. Pancras to Westminster there are two routes:
- Route A: Victoria line to Green Park (4 stations), then Jubilee line to Westminster (1 station).
- Route B: Piccadilly line to Green Park (6 stations), then Jubilee line to Westminster (1 station).
Route A looks best, but if there are engineering works on the Victoria line, Route B might be quicker. So the directions contain a choice:
IF there are engineering works on the Victoria Line
THEN
Take the Piccadilly Line to Green Park (6 stations)
Take the Jubilee Line to Westminster (1 station)
ELSE
Take the Victoria Line to Green Park (4 stations)
Take the Jubilee Line to Westminster (1 station)
ENDIF 🔬 Lab: follow the IF
Example answer: log on to your computer
- Press the power button and wait for the log-on screen.
- Click on your user name (or type it in).
- Type your password.
- Press Enter.
- IF the password is rejected THEN go back to Step 3 (after 3 failures, ask your teacher) ELSE wait for the desktop to appear.
Check your own answers: is every step one clear action? Is the order forced (could step 4 come before step 3)? Did you need a condition (IF) or a repeat?
Many problems have more than one solution. Sometimes the choice is a personal preference; sometimes one solution is measurably better.
✍️ 12.03 Expressing algorithms
We write a solution (an algorithm) as a sequence of steps in structured English or pseudocode, or draw it as a flowchart.
The four basic constructs
Most problems involve data: we input it, process it and output results (Figure 12.03), so we also need input and output statements.
Knowing the constructs tells you how detailed your design must be. Table 12.01 shows each one in all three notations.
| Construct | Structured English | Pseudocode |
|---|---|---|
| Assignment & sequence | SET A TO 34 INCREMENT B | A ← 34 B ← B + 1 |
| Selection | IF A IS GREATER THAN B THEN … ELSE … | IF A > B THEN … ELSE … ENDIF |
| Repetition | REPEAT UNTIL A IS EQUAL TO B … | REPEAT … UNTIL A = B |
| Input | INPUT A | INPUT "Prompt: " A |
| Output | OUTPUT "Message" OUTPUT B | OUTPUT "Message", B |
🎮 Which construct is it?
📦 12.04 Variables
When data is input, each value is stored in memory. To write statements about it, we need to name that memory location. A named memory location is a variable: think of a box with a name label on it.
In a number-guessing game, the count of guesses might be stored in NumberOfGuesses and the player's name in ThisPlayer.
- Identifiers contain no spaces: only letters, digits and _ (underscore).
- The name should say what the variable is used for, so it is often several words joined together.
- The convention used in the book is CamelCaps:
NumberOfGuesses,ThisPlayer. It makes long identifiers easy to read.
🎮 Valid identifier?
➡️ 12.05 Assignments
Assigning a value
INPUT Number stores the value that is typed in (say 15) in the variable Number. NumberOfGuesses ← 1 stores the value 1 in NumberOfGuesses.
Updating a value
NumberOfGuesses ← NumberOfGuesses + 1 takes the value stored in NumberOfGuesses, adds 1, and stores the new value back in the same variable. Read ← as "becomes".
Copying a value
Value2 ← Value1 copies the value from Value1 into Value2. Value1 keeps its value until something else is assigned to it.
Swapping two values
To swap the contents of two variables, you must park one value in a temporary variable first. Otherwise the second value to be moved is overwritten by the first.
Temp ← Value1 Value1 ← Value2 Value2 ← Temp
(a) copy Value1 into Temp; (b) copy Value2 into Value1; (c) copy Temp into Value2.
🔬 Lab: swap the boxes
Goal: finish with Value1 = 34 and Value2 = 15. Each button is one assignment. Try it first without Temp and see what happens.
Using input, output, assignment and sequence constructs
Problem: convert a distance in miles and output the equivalent distance in km.
Step 1: write the problem as structured English statements:
INPUT number of miles Calculate number of km OUTPUT calculated result as km
Step 2: analyse the data values needed. We need one variable for the distance in miles and one for the result of multiplying the miles by 1.61. List them in an identifier table:
| Identifier | Explanation |
|---|---|
| Miles | Distance as a whole number of miles |
| Km | The result from using the given formula: Km = Miles * 1.61 |
Table 12.02 Identifier table for miles to km conversion
Step 3: add detail with a flowchart or pseudocode. Both should show the same level of detail, using only the constructs in Table 12.01.
INPUT "Enter miles:" Miles Km ← Miles * 1.61 OUTPUT "km: ", Km
Model answer
| Identifier | Explanation |
|---|---|
| Inches | The length input, in inches |
| Centimetres | The result: Centimetres = Inches * 2.54 |
INPUT "Enter a length in inches: " Inches Centimetres ← Inches * 2.54 OUTPUT "Centimetres: ", Centimetres
🔀 12.06 Logic statements
The Underground directions in 12.02 followed one set of steps or another depending on a condition. The selection construct does exactly this (Figure 12.10):
IF A < B // ← a simple condition
THEN
<statement(s)>
ELSE
<statement(s)>
ENDIF Figure 12.10 Pseudocode for the selection construct
A condition contains at least one logic proposition (Chapter 4, Section 4.01). Propositions use the relational (comparison) operators:
| Operator | Comparison |
|---|---|
| = | Is equal to |
| < | Is less than |
| > | Is greater than |
| <= | Is less than or equal to |
| >= | Is greater than or equal to |
| <> | Is not equal to |
Table 12.03 Relational operators
Conditions are either TRUE or FALSE. In pseudocode, = tests for equality, while ← assigns a value. Don't mix them up.
A person is a child if they are under 13 and an adult if they are over 19. Between 13 and 19 inclusive, they are a teenager:
- If
Age < 13then person is a child. - If
Age > 19then person is an adult. - If
Age >= 13 AND Age <= 19then person is a teenager.
🔬 Lab: evaluate the conditions
🎮 Pick the condition for a free fare
Careful: the English says "under 12 and over 60", but no one can be both. Test each condition on these passengers.
| Age | Should be free? | Your condition says |
|---|
Model answer
If Age < 12 OR Age > 60 then fare is free. (Equivalently: If NOT (Age >= 12 AND Age <= 60) then fare is free.) Otherwise the passenger pays: If Age >= 12 AND Age <= 60 then fare is paid.
🎲 The number-guessing game
- The player inputs a number to guess the secret number stored.
- If the guess was correct, output a congratulations message.
- If the number input was larger than the secret number, output "secret number is smaller".
- If the number input was smaller than the secret number, output "secret number is greater".
SecretNumber ← Random // a whole number from 1 to 100
INPUT Guess
IF Guess = SecretNumber
THEN
OUTPUT "Well done. You have guessed the secret number"
ELSE
IF Guess > SecretNumber
THEN
OUTPUT "secret number is smaller"
ELSE
OUTPUT "secret number is greater"
ENDIF
ENDIF More complex conditions use the logical operators AND, OR and NOT. Suppose the player has several guesses, and after 10 wrong guesses a different message appears:
IF Guess = SecretNumber
THEN
OUTPUT "Well done. You have guessed the secret number"
ELSE
IF Guess <> SecretNumber AND NumberOfGuesses = 10 // complex condition
THEN
OUTPUT "You still have not guessed the secret number"
ELSE
IF Guess > SecretNumber
THEN
OUTPUT "secret number is smaller"
ELSE
OUTPUT "secret number is greater"
ENDIF
ENDIF
ENDIF NumberofGuesses here; it should be NumberOfGuesses everywhere. Also, inside that ELSE we already know Guess <> SecretNumber, so NumberOfGuesses = 10 on its own would do.Using selection constructs
Problem: take three numbers as input and output the largest number. One method:
- Input all three numbers at the beginning.
- Store each input value in a separate variable (Table 12.04).
- Compare the first number with the second, then compare the bigger of these with the third.
- Output the bigger number from this second comparison.
| Identifier | Explanation |
|---|---|
| Number1 | The first number to be input |
| Number2 | The second number to be input |
| Number3 | The third number to be input |
Table 12.04 Identifier table for biggest number problem
INPUT Number1
INPUT Number2
INPUT Number3
IF Number1 > Number2
THEN
// Number1 is bigger
IF Number1 > Number3
THEN
OUTPUT Number1
ELSE
OUTPUT Number3
ENDIF
ELSE
// Number2 is bigger
IF Number2 > Number3
THEN
OUTPUT Number2
ELSE
OUTPUT Number3
ENDIF
ENDIF When an IF statement contains another IF statement, they are nested IF statements.
Answer
Change every > to <: IF Number1 < Number2, IF Number1 < Number3, IF Number2 < Number3. Update the comments to "is smaller" too. The structure stays the same.
Using selection constructs (alternative method)
Same problem, different algorithm:
- Input the first number and store it in
BiggestSoFar. - Input the second number and compare it with
BiggestSoFar. - If the second number is bigger, assign its value to
BiggestSoFar. - Input the third number and compare it with
BiggestSoFar. - If the third number is bigger, assign its value to
BiggestSoFar. - Output the value stored in
BiggestSoFar.
| Identifier | Explanation |
|---|---|
| BiggestSoFar | Stores the biggest number input so far |
| NextNumber | The next number to be input |
Table 12.05 Identifier table for the alternative solution
INPUT BiggestSoFar
INPUT NextNumber
IF NextNumber > BiggestSoFar
THEN
BiggestSoFar ← NextNumber
ENDIF
INPUT NextNumber
IF NextNumber > BiggestSoFar
THEN
BiggestSoFar ← NextNumber
ENDIF
OUTPUT BiggestSoFar When the third number is input, the second is overwritten because it is no longer needed.
- Only two variables are used.
- The IFs are not nested and have no ELSE, so they are easier to understand.
- It adapts easily to more numbers (Worked Example 12.04).
BiggestSoFar changes; if the third is bigger again, it changes again. Depending on the inputs, that's up to two extra assignments.🔁 12.07 Loops
In Worked Example 12.03 the two IF statements are identical. Comparing 10 numbers would need that IF nine times; 100 numbers would be very tedious. A repetition construct (a loop) avoids writing the same lines over and over.
Repetition using REPEAT…UNTIL
Problem: take 10 numbers as input and output the largest number. We need one more variable: a counter, so we know when we have compared 10 numbers.
| Identifier | Explanation |
|---|---|
| BiggestSoFar | Stores the biggest number input so far |
| NextNumber | The next number to be input |
| Counter | Stores how many numbers have been input so far |
Table 12.06 Identifier table using REPEAT…UNTIL
INPUT BiggestSoFar
Counter ← 1
REPEAT
INPUT NextNumber
Counter ← Counter + 1
IF NextNumber > BiggestSoFar
THEN
BiggestSoFar ← NextNumber
ENDIF
UNTIL Counter = 10
OUTPUT BiggestSoFar Each new number overwrites the previous one, which is no longer needed.
Answer
(a) Change UNTIL Counter = 10 to UNTIL Counter = 100.
(b) Add INPUT HowMany as the very first line (add it to the identifier table) and change the last test to UNTIL Counter = HowMany. Note that REPEAT always runs at least once, so this still assumes HowMany is at least 2.
Another loop does the counting for us: FOR…NEXT.
Repetition using FOR…NEXT
Same problem and identifiers, but now Counter counts the number of times round the loop.
Table 12.07: BiggestSoFar · NextNumber · Counter (counts the number of times round the loop)
INPUT BiggestSoFar
FOR Counter ← 2 TO 10
INPUT NextNumber
IF NextNumber > BiggestSoFar
THEN
BiggestSoFar ← NextNumber
ENDIF
NEXT Counter
OUTPUT BiggestSoFar First time round, Counter is 2. Next time it has automatically increased to 3, and so on; the last time round it is 10.
Repetition using a rogue value
Problem: a sequence of non-zero numbers is terminated by 0. Take this sequence as input and output the largest number.
The rogue value here is 0. Choose a rogue value of the same type but outside the normal range: if 0 could be real data, use something like −1. Instead of counting, the loop checks whether the input is 0.
Table 12.08: BiggestSoFar · NextNumber
INPUT BiggestSoFar
REPEAT
INPUT NextNumber
IF NextNumber > BiggestSoFar
THEN
BiggestSoFar ← NextNumber
ENDIF
UNTIL NextNumber = 0
OUTPUT BiggestSoFar INPUT NextNumber
BiggestSoFar ← NextNumber
WHILE NextNumber <> 0 DO // sequence terminator not encountered
INPUT NextNumber
IF NextNumber > BiggestSoFar
THEN
BiggestSoFar ← NextNumber
ENDIF
ENDWHILE
OUTPUT BiggestSoFar The REPEAT version works even for one non-zero input, but not when the only input is 0. Then we don't want the loop body to run at all, so we use WHILE…ENDWHILE. The first number goes into both NextNumber and BiggestSoFar; if it is zero, the loop body is skipped. For a non-zero first number, it does the same as REPEAT…UNTIL.
0 in the inputs for each version. The REPEAT version waits for another number, because it always runs its body once.Implementing the number-guessing game with a loop
- The player repeatedly inputs a number to guess the secret number stored.
- If the guess is correct, the number of guesses made is output and the game stops.
- If the number input is larger than the secret number, the player is told to input a smaller number.
- If the number input is smaller, the player is told to input a larger number.
Structured English:
SET value for secret number
REPEAT the following UNTIL correct guess
INPUT guess
count number of guesses
COMPARE guess with secret number
OUTPUT comment
OUTPUT number of guesses | Identifier | Explanation |
|---|---|
| SecretNumber | The number to be guessed |
| NumberOfGuesses | The number of guesses the player has made |
| Guess | The number the player has input as a guess |
Table 12.09 Identifier table for number-guessing game
Pseudocode with a post-condition loop:
SecretNumber ← Random // a whole number from 1 to 100
NumberOfGuesses ← 0
REPEAT
INPUT Guess
NumberOfGuesses ← NumberOfGuesses + 1
IF Guess > SecretNumber
THEN
OUTPUT "Input a smaller number"
ENDIF
IF Guess < SecretNumber
THEN
OUTPUT "Input a larger number"
ENDIF
UNTIL Guess = SecretNumber
OUTPUT NumberOfGuesses Pseudocode with a pre-condition loop (this is the one that matches the flowchart):
SecretNumber ← Random // a whole number from 1 to 100
INPUT Guess
NumberOfGuesses ← 1
WHILE Guess <> SecretNumber DO
IF Guess > SecretNumber
THEN
OUTPUT "Input a smaller number"
ENDIF
IF Guess < SecretNumber
THEN
OUTPUT "Input a larger number"
ENDIF
INPUT Guess
NumberOfGuesses ← NumberOfGuesses + 1
ENDWHILE
OUTPUT NumberOfGuesses // the player is given the message to input a smaller number). We've turned them into OUTPUT statements so the lab can show them. Notice NumberOfGuesses starts at 0 in the REPEAT version but at 1 in the WHILE version, because there the first guess is input before the loop.Calculating running totals and averages
Problem: take 10 numbers as input and output their sum and their average.
| Identifier | Explanation |
|---|---|
| RunningTotal | Stores the sum of the numbers input so far |
| Counter | How many numbers have been input |
| NextNumber | The next number input |
| Average | The average of the numbers input |
Table 12.10 Identifier table for running total and average
RunningTotal ← 0
FOR Counter ← 1 TO 10
INPUT NextNumber
RunningTotal ← RunningTotal + NextNumber
NEXT Counter
OUTPUT RunningTotal
Average ← RunningTotal / 10
OUTPUT Average It is very important to initialise RunningTotal to zero before adding the numbers.
🎮 Which loop fits best?
Model answer
We no longer know how many numbers there are, so the FOR loop becomes a WHILE loop and Counter has to be counted by hand. The average divides by Counter, and only when at least one number was input.
RunningTotal ← 0
Counter ← 0
INPUT NextNumber
WHILE NextNumber <> 0 DO
RunningTotal ← RunningTotal + NextNumber
Counter ← Counter + 1
INPUT NextNumber
ENDWHILE
OUTPUT RunningTotal
IF Counter > 0
THEN
Average ← RunningTotal / Counter
OUTPUT Average
ENDIF Using nested loops
Problem: take as input two numbers and a symbol. Output a grid made entirely of the symbol, with the number of rows given by the first number and the number of columns by the second. Inputs 3, 7 and & give:
&&&&&&& &&&&&&& &&&&&&&
| Identifier | Explanation |
|---|---|
| NumberOfRows | Stores the number of rows of the grid |
| NumberOfColumns | Stores the number of columns of the grid |
| Symbol | Stores the chosen character symbol |
| RowCounter | Counts the number of rows |
| ColumnCounter | Counts the number of columns |
Table 12.11 Identifier table for the nested loop example
INPUT NumberOfRows
INPUT NumberOfColumns
INPUT Symbol
FOR RowCounter ← 1 TO NumberOfRows
FOR ColumnCounter ← 1 TO NumberOfColumns
OUTPUT Symbol // without moving to next line
NEXT ColumnCounter
OUTPUT Newline // move to the next line
NEXT RowCounter Each time round the outer loop (rows), the inner loop runs completely, outputting one symbol per column. This is a nested loop.
🪜 12.08 Stepwise refinement
Bigger problems are easier if you break them into smaller steps, and break those down again until each step is easy. To be programmable, every step must end up as one of the basic constructs: sequence, assignment, selection, repetition, input or output.
Stepwise refinement does this to an outline solution. The cake recipe in 12.02 was an example: "mix the ingredients" was refined into Steps 2.1 to 2.4.
Drawing a pyramid using stepwise refinement
Problem: take as input a chosen symbol and an odd number. Output a pyramid made entirely of the symbol, with the number of symbols in the final row equal to the number input. Inputs A and 9 give:
A AAA AAAAA AAAAAAA AAAAAAAAA
It is like Worked Example 12.09, but the symbols per row start at 1 and go up by 2 each row, and each row starts with one fewer space to create the slope. First attempt, in structured English:
01 Set up initial values 02 REPEAT 03 Output number of spaces 04 Output number of symbols 05 Adjust number of spaces and number of symbols to be output in next row 06 UNTIL the required number of symbols have been output in one row
The steps are numbered so we can refer to them. This isn't detailed enough to program: exactly what values do we need?
- Input: the symbol, and the number of symbols in the final row (odd, so the pyramid is symmetrical).
- Spaces in the first row: half of (final row's symbols − 1). Symbols in the first row: 1.
| Identifier | Explanation |
|---|---|
| Symbol | The character symbol to form the pyramid |
| MaxNumberOfSymbols | The number of symbols in the final row |
| NumberOfSpaces | The number of spaces to be output in the current row |
| NumberOfSymbols | The number of symbols to be output in the current row |
Table 12.12 Identifier table for pyramid example
Open each outline step to see how it is refined. The numbering shows which step is being refined.
01 // Set up initial values expands into: 01.1 INPUT Symbol 01.2 INPUT MaxNumberOfSymbols 01.3 NumberOfSpaces ← (MaxNumberOfSymbols - 1) / 2 01.4 NumberOfSymbols ← 1
We need an odd number for MaxNumberOfSymbols, so Step 01.2 is refined again:
01.2 // INPUT MaxNumberOfSymbols expands into: 01.2.1 REPEAT 01.2.2 INPUT MaxNumberOfSymbols 01.2.3 UNTIL MaxNumberOfSymbols MOD 2 = 1 01.2.4 // MOD 2 gives the remainder after integer division by 2
03 // Output number of spaces expands into: 03.1 FOR i ← 1 TO NumberOfSpaces 03.2 OUTPUT Space // without moving to next line 03.3 NEXT i
04 // Output number of symbols expands into: 04.1 FOR i ← 1 TO NumberOfSymbols 04.2 OUTPUT Symbol // without moving to next line 04.3 NEXT i 04.4 OUTPUT Newline // move to the next line
Decrease the spaces by 1 and increase the symbols by 2:
05 // Adjust values for next row expands into: 05.1 NumberOfSpaces ← NumberOfSpaces - 1 05.2 NumberOfSymbols ← NumberOfSymbols + 2
Check whether the number of symbols for the next row is now greater than the value input at the beginning:
06 UNTIL NumberOfSymbols > MaxNumberOfSymbols
Putting all the steps together:
01 // Set Values 01.1 INPUT Symbol 01.2 // Input max number of symbols (an odd number) 01.2.1 REPEAT 01.2.2 INPUT MaxNumberOfSymbols 01.2.3 UNTIL MaxNumberOfSymbols MOD 2 = 1 01.3 NumberOfSpaces ← (MaxNumberOfSymbols - 1) / 2 01.4 NumberOfSymbols ← 1 02 REPEAT 03 // Output number of spaces 03.1 FOR i ← 1 TO NumberOfSpaces 03.2 OUTPUT Space // without moving to next line 03.3 NEXT i 04 // Output number of symbols 04.1 FOR i ← 1 TO NumberOfSymbols 04.2 OUTPUT Symbol // without moving to next line 04.3 NEXT i 04.4 OUTPUT Newline // move to the next line 05 // Adjust Values For Next Row 05.1 NumberOfSpaces ← NumberOfSpaces - 1 05.2 NumberOfSymbols ← NumberOfSymbols + 2 06 UNTIL NumberOfSymbols > MaxNumberOfSymbols
🔬 Lab: pyramid and hollow triangle
| Row | NumberOfSpaces | NumberOfSymbols |
|---|
Spaces are shown as faint dots so you can count them.
A A A A A A A AAAAAAAAAA first attempt in structured English:
01 Set up initial values 02 REPEAT 03 Output leading number of spaces 04 Output symbol, middle spaces, symbol 05 Adjust number of spaces and number of symbols to be output in next row 06 UNTIL the required number of symbols have been output in one row
Model answer
Keep track of MiddleSpaces as well as the leading spaces. Top row: one symbol only. Bottom row (no leading spaces): a full row of symbols. Every other row: symbol, MiddleSpaces spaces, symbol, with MiddleSpaces going −1, 1, 3, 5, …
01 // Set up initial values 01.1 INPUT Symbol 01.2.1 REPEAT 01.2.2 INPUT MaxNumberOfSymbols 01.2.3 UNTIL MaxNumberOfSymbols MOD 2 = 1 01.3 NumberOfSpaces ← (MaxNumberOfSymbols - 1) / 2 01.4 MiddleSpaces ← -1 // the top row has no middle 02 REPEAT 03 // Output leading number of spaces 03.1 FOR i ← 1 TO NumberOfSpaces 03.2 OUTPUT Space // without moving to next line 03.3 NEXT i 04 // Output symbol, middle spaces, symbol 04.1 IF NumberOfSpaces = 0 04.2 THEN // bottom row: a full line of symbols 04.3 FOR i ← 1 TO MaxNumberOfSymbols 04.4 OUTPUT Symbol // without moving to next line 04.5 NEXT i 04.6 ELSE 04.7 OUTPUT Symbol // without moving to next line 04.8 IF MiddleSpaces > 0 04.9 THEN 04.10 FOR i ← 1 TO MiddleSpaces 04.11 OUTPUT Space // without moving to next line 04.12 NEXT i 04.13 OUTPUT Symbol // without moving to next line 04.14 ENDIF 04.15 ENDIF 04.16 OUTPUT Newline // move to the next line 05 // Adjust values for next row 05.1 NumberOfSpaces ← NumberOfSpaces - 1 05.2 MiddleSpaces ← MiddleSpaces + 2 06 UNTIL NumberOfSpaces < 0
🧩 12.09 Modules
Another way to develop a solution is to decompose the problem into sub-tasks. Each sub-task is a module that is refined separately. Modules are procedures and functions.
A procedure groups some steps and gives them a name (identifier). To perform those steps, we call the procedure by its name.
A function also groups steps under a name, but they produce and return a value that is used in an expression.
Because a function returns a value, its definition states that value's data type (Chapter 13). Module identifiers follow the same rules as variable identifiers (12.04).
Drawing a pyramid using modules
Same problem as Worked Example 12.10. To set up the initial values we call a procedure: CALL SetValues. The top level uses one procedure per step, so each procedure has to be defined:
CALL SetValues
REPEAT
CALL OutputSpaces
CALL OutputSymbols
CALL AdjustValuesForNextRow
UNTIL NumberOfSymbols > MaxNumberOfSymbols
PROCEDURE SetValues
INPUT Symbol
CALL InputMaxNumberOfSymbols // need to ensure it is an odd number
NumberOfSpaces ← (MaxNumberOfSymbols - 1) / 2
NumberOfSymbols ← 1
ENDPROCEDURE
PROCEDURE InputMaxNumberOfSymbols
REPEAT
INPUT MaxNumberOfSymbols
UNTIL MaxNumberOfSymbols MOD 2 = 1
ENDPROCEDURE
PROCEDURE OutputSpaces
FOR Count1 ← 1 TO NumberOfSpaces
OUTPUT Space // without moving to next line
NEXT Count1
ENDPROCEDURE
PROCEDURE OutputSymbols
FOR Count2 ← 1 TO NumberOfSymbols
OUTPUT Symbol // without moving to next line
NEXT Count2
OUTPUT Newline // move to the next line
ENDPROCEDURE
PROCEDURE AdjustValuesForNextRow
NumberOfSpaces ← NumberOfSpaces - 1
NumberOfSymbols ← NumberOfSymbols + 2
ENDPROCEDURE Model answer
One procedure per job: set up, add one number, output the results. (If your teacher meant Task 12.06, use the same idea: SetValues, OutputLeadingSpaces, OutputRow, AdjustValues.)
CALL Initialise
INPUT NextNumber
WHILE NextNumber <> 0 DO
CALL AddToTotal
INPUT NextNumber
ENDWHILE
CALL OutputResults
PROCEDURE Initialise
RunningTotal ← 0
Counter ← 0
ENDPROCEDURE
PROCEDURE AddToTotal
RunningTotal ← RunningTotal + NextNumber
Counter ← Counter + 1
ENDPROCEDURE
PROCEDURE OutputResults
OUTPUT RunningTotal
IF Counter > 0
THEN
OUTPUT RunningTotal / Counter
ENDIF
ENDPROCEDURE Drawing a pyramid using procedures and functions
Same problem again. The top level now calls three procedures and uses two functions, in lines 05.1 and 05.2. Procedure SetValues also uses a function, ValidatedMaxNumberOfSymbols.
01 CALL SetValues
02 REPEAT
03 CALL OutputSpaces
04 CALL OutputSymbols
05.1 NumberOfSpaces ← AdjustedNumberOfSpaces
05.2 NumberOfSymbols ← AdjustedNumberOfSymbols
06 UNTIL NumberOfSymbols > MaxNumberOfSymbols
PROCEDURE SetValues
INPUT Symbol
MaxNumberOfSymbols ← ValidatedMaxNumberOfSymbols
NumberOfSpaces ← (MaxNumberOfSymbols - 1) / 2
NumberOfSymbols ← 1
ENDPROCEDURE
FUNCTION ValidatedMaxNumberOfSymbols RETURNS INTEGER
REPEAT
INPUT MaxNumberOfSymbols
UNTIL MaxNumberOfSymbols MOD 2 = 1
RETURN MaxNumberOfSymbols
ENDFUNCTION
PROCEDURE OutputSpaces
FOR Count1 ← 1 TO NumberOfSpaces
OUTPUT Space // without moving to next line
NEXT Count1
ENDPROCEDURE
PROCEDURE OutputSymbols
FOR Count2 ← 1 TO NumberOfSymbols
OUTPUT Symbol // without moving to next line
NEXT Count2
OUTPUT Newline // move to the next line
ENDPROCEDURE
FUNCTION AdjustedNumberOfSpaces RETURNS INTEGER
NumberOfSpaces ← NumberOfSpaces - 1
RETURN NumberOfSpaces
ENDFUNCTION
FUNCTION AdjustedNumberOfSymbols RETURNS INTEGER
NumberOfSymbols ← NumberOfSymbols + 2
RETURN NumberOfSymbols
ENDFUNCTION AdjustedNumbeOfSymbols (missing "r"), but the function is defined as AdjustedNumberOfSymbols. A real program would stop with "unknown identifier".A benefit of modules is that they can be reused in other solutions. So modules should be self-contained: they shouldn't rely on outside variables. Everything a module needs should be passed to it as parameters.
Drawing a pyramid using modules and parameters
Same problem as Worked Example 12.12, but every value a module needs is passed in as a parameter.
01 CALL SetValues(Symbol, MaxNumberOfSymbols, NumberOfSpaces, NumberOfSymbols)
02 REPEAT
03 CALL OutputSpaces(NumberOfSpaces)
04 CALL OutputSymbols(NumberOfSymbols, Symbol)
05.1 NumberOfSpaces ← AdjustedNumberOfSpaces(NumberOfSpaces)
05.2 NumberOfSymbols ← AdjustedNumberOfSymbols(NumberOfSymbols)
06 UNTIL NumberOfSymbols > MaxNumberOfSymbols
PROCEDURE SetValues(BYREF Symbol : CHAR, MaxNumberOfSymbols : INTEGER, NumberOfSpaces : INTEGER, NumberOfSymbols : INTEGER)
INPUT Symbol
MaxNumberOfSymbols ← ValidatedMaxNumberOfSymbols
NumberOfSpaces ← (MaxNumberOfSymbols - 1) / 2
NumberOfSymbols ← 1
ENDPROCEDURE
FUNCTION ValidatedMaxNumberOfSymbols RETURNS INTEGER
DECLARE MaxSymbols : INTEGER
REPEAT
INPUT MaxSymbols
UNTIL MaxSymbols MOD 2 = 1
RETURN MaxSymbols
ENDFUNCTION
PROCEDURE OutputSpaces(NumberOfSpaces : INTEGER)
DECLARE Count1 : INTEGER
FOR Count1 ← 1 TO NumberOfSpaces
OUTPUT Space // without moving to next line
NEXT Count1
ENDPROCEDURE
PROCEDURE OutputSymbols(NumberOfSymbols : INTEGER, Symbol : CHAR)
DECLARE Count2 : INTEGER
FOR Count2 ← 1 TO NumberOfSymbols
OUTPUT Symbol // without moving to next line
NEXT Count2
OUTPUT Newline // move to the next line
ENDPROCEDURE
FUNCTION AdjustedNumberOfSpaces(NumberOfSpaces : INTEGER) RETURNS INTEGER
NumberOfSpaces ← NumberOfSpaces - 1
RETURN NumberOfSpaces
ENDFUNCTION
FUNCTION AdjustedNumberOfSymbols(NumberOfSymbols : INTEGER) RETURNS INTEGER
NumberOfSymbols ← NumberOfSymbols + 2
RETURN NumberOfSymbols
ENDFUNCTION SetValues must send its four values back to the caller, so its parameters have to be passed BYREF (by reference, Chapter 14). In the book they look like ordinary (BYVAL) parameters, and the main program would never receive the values. In Cambridge pseudocode, BYREF applies to every parameter after it. (2) The book again spells it AdjustedNumbeOfSymbols; fixed here. We also added DECLARE lines so Count1, Count2 and MaxSymbols are really local. Watch them appear under Local in the lab.Procedure OutputSpaces uses Count1 only inside the module, and OutputSymbols uses Count2 only inside the module. Such a variable is a local variable (Chapter 14, Section 14.09). A variable available to all modules is a global variable.
🧪 Pseudocode lab
Pick a program, then press Step to run it one line at a time. The highlighted line is about to run. Watch the variables change and the output appear. Inputs are taken from the box on the right, left to right; if it runs out, the program asks you. Press Edit to change the code or write your own (Cambridge 9618 style: ← or <-, IF/THEN/ELSE/ENDIF, REPEAT/UNTIL, WHILE/DO/ENDWHILE, FOR/TO/NEXT, PROCEDURE, FUNCTION/RETURNS/RETURN, CALL, BYREF, DECLARE, MOD, DIV, AND/OR/NOT).
Inputs waiting
Variables
Output
Random: the book writes SecretNumber ← Random. In this lab it gives a whole number from 1 to 100. (In real Cambridge pseudocode, RAND(100) gives a real number 0 ≤ x < 100, so you would use INT(RAND(100)) + 1.) Space outputs one space and OUTPUT Newline moves to a new line. A comment containing "without moving to next line" keeps the output on the same line, as in the book.📌 Summary
- Abstraction involves filtering out information that is not needed to solve the problem.
- Decomposition is breaking problems down into sub-problems, leading to the concept of a program module.
- An algorithm is a sequence of steps that can be carried out to solve a problem.
- Algorithms are expressed using the four basic constructs of assignment, sequence, selection and repetition.
- Algorithms can be documented using pseudocode.
- Stepwise refinement: breaking down the steps of an outline solution into smaller and smaller steps.
- Logic statements use the relational operators
=,<,>,<>,<=and>=and the logic operators AND, OR and NOT. - Selection constructs and conditional loops use conditions to decide which steps are followed.
🃏 Key-term flashcards
Tap a card to flip it.
✅ Quick quiz
🔢 Modulo-11 practice
For Exam-style Question 1. Weights go 10, 9, 8 … 2 from the left. Work out the check digit, then check.
📝 Exam-style questions
Write your answer first, then open the model answer. These are my mark-scheme-style answers, not official mark schemes. Each one runs in the lab.
Weighting ← 10
Total ← 0
FOR Count ← 1 TO 9
INPUT Digit
Value ← Digit * Weighting
Total ← Total + Value
Weighting ← Weighting - 1
NEXT Count
Remainder ← Total MOD 11
CheckDigit ← 11 - Remainder
IF CheckDigit = 10
THEN
CheckDigit ← "X"
ENDIF
OUTPUT CheckDigit Mark points: the three initial values · a loop that runs 9 times (FOR, or REPEAT … UNTIL Count = 9 with Count ← Count + 1) · INPUT Digit · Value calculation · running Total · decrement Weighting · Remainder using MOD · CheckDigit ← 11 − Remainder · IF CheckDigit = 10 THEN CheckDigit ← "X".
CheckDigit ← (11 - Remainder) MOD 11, so it becomes 0. The flowchart also never outputs the check digit; our answer adds OUTPUT CheckDigit.PositiveCount ← 0
NegativeCount ← 0
REPEAT
INPUT Number
IF Number > 0
THEN
PositiveCount ← PositiveCount + 1
ENDIF
IF Number < 0
THEN
NegativeCount ← NegativeCount + 1
ENDIF
UNTIL Number = 0
OUTPUT "Positive: ", PositiveCount
OUTPUT "Negative: ", NegativeCount Mark points: initialise both counts to 0 · REPEAT … UNTIL Number = 0 · INPUT inside the loop · test for positive · increment PositiveCount · test for negative (a separate IF, or ELSE IF, so 0 is counted in neither) · increment NegativeCount.
RogueValue ← -1
Total ← 0
Count ← 0
INPUT Number
WHILE Number <> RogueValue DO
Count ← Count + 1
Total ← Total + Number
INPUT Number
ENDWHILE
IF Count > 0
THEN
Average ← Total / Count
OUTPUT Average
ENDIF Mark points: RogueValue ← −1 · Total and Count ← 0 · first INPUT before the loop · WHILE Number <> RogueValue · increment Count and add to Total · INPUT Number at the end of the loop body · ENDWHILE · IF Count > 0 then calculate and output Average.
Guru Jazzy · Cambridge AS & A Level Computer Science · Chapter 12 Algorithm Design and Problem-Solving · Figures from the Cambridge coursebook (Langfield & Duddell)