Skip to main content
Editor

Developing Algorithms: Pseudocode, MOD, and Building Blocks

Beginner35 min3 exercises45 XP
0/3 exercises

An algorithm is a finite set of instructions that accomplishes one specific task, and you can write the same algorithm in plain English, draw it as a flowchart, or type it as code. The recipe card for pancakes, the steps your phone follows to sort your photos by date, and the rules a referee uses to decide a tie are all algorithms.

What counts as an algorithm?

Two words in the definition matter. Finite means the steps run out: an algorithm always comes to an end. Specific task means it has a clear job, like "find the highest score" or "decide if this year is a leap year".

Here is an algorithm written in natural language, which means everyday words:

  • Set the total to 0.
  • If there are no scores left, go to step 5.
  • Add the next score to the total.
  • Go back to step 2.
  • Display the total.
  • The same steps can be drawn as a diagram. In a flowchart, ovals mark the start and end, rectangles are actions, and diamonds are yes-or-no questions:

    ( Start )
        |
    [ total ← 0 ]
        |
    < Any scores left? > -- no --> [ DISPLAY(total) ] --> ( End )
        | yes
    [ total ← total + next score ]
        |
    ( back up to "Any scores left?" )

    It can also be written as pseudocode, a halfway point between English and a programming language. The exam uses its own pseudocode, printed on a reference sheet you get during the test:

    Pseudocode: add up a list of scores
    total ← 0
    FOR EACH score IN scores
    {
    total ← total + score
    }
    DISPLAY(total)

    Natural language, diagrams and pseudocode are for people. For a computer to carry out an algorithm, someone implements it in a programming language. That language can be text-based, like Python, or visual, like the drag-and-drop blocks in Scratch. The exam shows its pseudocode in both a text form and a block form.

    Look back at the flowchart and you can spot three ideas. Steps that follow one after another are sequencing. The diamond that picks a path is selection. The arrow that loops back is iteration. Every algorithm, from a calculator app to a game's enemy movement, can be built from combinations of these three.

    Why does the order of the steps matter?

    Sequencing means each step is carried out in the order the statements are written, top to bottom. A code statement is one part of a program that tells the computer to do an action, like score ← score + 10 or DISPLAY(score).

    Order can change the answer completely. Suppose you want two variables to trade values. This pseudocode looks reasonable but fails:

    Pseudocode: a swap that does not work
    a ← "cereal"
    b ← "toast"
    a ← b
    b ← a

    After line 3, a holds "toast" and the word "cereal" is gone for good. Line 4 then copies "toast" back into b. The fix is to save one value in a third variable before anything is overwritten. Run both versions:

    Swapping two values: order matters
    breakfast = "cereal"
    lunch = "toast"
    # Wrong order: the first value is overwritten before it is saved.
    a = breakfast
    b = lunch
    a = b
    b = a
    print("Wrong swap:", a, b)
    # Right order: keep a copy of a in temp before changing a.
    a = breakfast
    b = lunch
    temp = a
    a = b
    b = temp
    print("Right swap:", a, b)
    Output
    Wrong swap: toast toast
    Right swap: toast cereal

    Clear writing matters too. Meaningful names like temp and total, one action per line, and steps in a sensible order make an algorithm readable for your teammates, your teacher, and you next month.

    How does a computer work out an expression?

    An expression is anything that produces a value. It can be a single value like 7, a variable like score, values joined by operators like score + 7, or a call to a procedure that returns a value, like LENGTH(names). However long it is, an expression is evaluated down to one single value.

    The arithmetic operators on the reference sheet are +, -, *, / and MOD. Division keeps the decimal part, so 9 / 4 is 2.25, the same as in Python.

    a MOD b gives the remainder when a is divided by b. For example, 23 MOD 4 is 3, because 4 goes into 23 five times (that makes 20) with 3 left over. The exam only uses MOD with a a whole number of 0 or more and b a whole number greater than 0. In Python, MOD is written %.

    Expressions follow an order of operations, set by each programming language. In the exam pseudocode it is the order you know from math class: parentheses first, then *, / and MOD (all at the same level, worked left to right), then + and - (also left to right). So 2 + 14 MOD 4 * 3 is worked out like this:

  • 14 MOD 4 is 2 (MOD and * are at the same level, and MOD comes first reading left to right).
  • 2 * 3 is 6.
  • 2 + 6 is 8.
  • Python's % sits at the same level as * and /, so Python gets 8 too:

    Arithmetic, MOD and the order of operations
    print(9 / 4) # division keeps the decimal part
    print(23 % 4) # remainder: 23 = 4 x 5 + 3
    print(2 + 14 % 4 * 3) # % and * left to right, then +
    print((2 + 14) % 4 * 3) # parentheses first: 16 % 4 is 0
    for n in [10, 15, 21, 30]:
    print(f"{n}: even? {n % 2 == 0}, multiple of 5? {n % 5 == 0}, "
    f"last digit {n % 10}")
    Output
    2.25
    3
    8
    0
    10: even? True, multiple of 5? True, last digit 0
    15: even? False, multiple of 5? True, last digit 5
    21: even? False, multiple of 5? False, last digit 1
    30: even? True, multiple of 5? True, last digit 0

    Those last lines show why MOD is so useful. n MOD 2 = 0 tests for even numbers, n MOD d = 0 tests whether n divides evenly by d, and n MOD 10 pulls out the last digit. MOD also makes values wrap around, like the days of the week or a 24-hour clock:

    MOD makes values wrap around
    days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
    friday = 4 # Python counts list positions from 0
    for wait in [2, 3, 10]:
    print(f"{wait} days after Fri is {days[(friday + wait) % 7]}")
    start_hour = 9
    for extra in [2, 5, 20]:
    print(f"{extra} hours after 09:00 is {(start_hour + extra) % 24:02d}:00")
    Output
    2 days after Fri is Sun
    3 days after Fri is Mon
    10 days after Fri is Mon
    2 hours after 09:00 is 11:00
    5 hours after 09:00 is 14:00
    20 hours after 09:00 is 05:00

    Can two different algorithms do the same job?

    Yes. Algorithms can be written in different ways and still accomplish the same task, and completely different algorithms can solve the same problem.

    A common case is a conditional that sets a true-or-false variable. Some conditional statements can be rewritten as one Boolean expression. A cinema gives a discount to children under 13 and to people 65 or older:

    Pseudocode: a discount rule written with IF statements
    IF(age < 13)
    {
    discount ← true
    }
    ELSE
    {
    IF(age ≥ 65)
    {
    discount ← true
    }
    ELSE
    {
    discount ← false
    }
    }

    The whole thing does the same as one line: discount ← (age < 13) OR (age ≥ 65). It works the other way too: a Boolean expression such as canVote ← (age ≥ 18) AND registered can be rewritten as nested IF statements that set canVote to true only when both parts hold.

    How can you be sure two versions really agree? Try every input that matters and compare:

    Checking that two versions always agree
    def discount_with_ifs(age):
    if age < 13:
    return True
    else:
    if age >= 65:
    return True
    else:
    return False
    def discount_with_boolean(age):
    return age < 13 or age >= 65
    disagree = []
    for age in range(121):
    if discount_with_ifs(age) != discount_with_boolean(age):
    disagree.append(age)
    print("Ages checked: 0 to 120")
    print("Ages where the versions disagree:", disagree)
    Output
    Ages checked: 0 to 120
    Ages where the versions disagree: []

    The same idea applies to whole algorithms. To find the highest score you could keep the biggest value seen so far, or you could sort the scores and take the last one. Both are correct:

    Two different algorithms, one answer
    scores = [72, 95, 88, 61, 95, 79]
    # Algorithm 1: keep the biggest value seen so far.
    biggest = scores[0]
    for score in scores:
    if score > biggest:
    biggest = score
    print("Keep the biggest:", biggest)
    # Algorithm 2: sort a copy of the list, then take the last value.
    print("Sort, then take the last:", sorted(scores)[-1])
    Output
    Keep the biggest: 95
    Sort, then take the last: 95

    The reverse warning matters on the exam too: algorithms that look similar can give different results. Here are two ways to label a temperature. The only difference is an else:

    Similar-looking algorithms, different results
    def label_two_ifs(temp):
    label = "cold"
    if temp > 30:
    label = "hot"
    if temp > 20:
    label = "warm"
    return label
    def label_if_else(temp):
    if temp > 30:
    return "hot"
    elif temp > 20:
    return "warm"
    return "cold"
    for temp in [15, 25, 35]:
    print(f"{temp}°C two ifs: {label_two_ifs(temp)} if-else: {label_if_else(temp)}")
    Output
    15°C  two ifs: cold  if-else: cold
    25°C  two ifs: warm  if-else: warm
    35°C  two ifs: warm  if-else: hot

    At 35°C, the first version sets "hot" and then the second IF also runs, because 35 is greater than 20, so "hot" is replaced by "warm". With else (Python's elif is an ELSE with an IF inside it), only one branch can run. When you compare two code segments on the exam, test a value that makes more than one condition true.

    How do you build a new algorithm from old ones?

    New algorithms come from three places: a fresh idea, combining algorithms you already have, or modifying one that almost does the job. Knowing a few classic algorithms by heart makes the last two much easier. These come up again and again:

  • finding the maximum or minimum of two or more numbers;
  • computing the sum or average of two or more numbers;
  • checking whether one whole number is evenly divisible by another (with MOD);
  • finding a robot's path through a maze, which has its own lesson: grid robot algorithms.
  • Here is the maximum algorithm in exam pseudocode. It starts with the first element, not with 0, so it also works when every number is negative:

    Pseudocode: the maximum of a list
    PROCEDURE findMax(numbers)
    {
    biggest ← numbers[1]
    FOR EACH n IN numbers
    {
    IF(n > biggest)
    {
    biggest ← n
    }
    }
    RETURN(biggest)
    }

    Now combine. A school raffle gives a prize to every ticket number divisible by 3, and the organizers want the largest winning number and the average of the winning numbers. Each piece below is a small, tested procedure, and the new algorithm is a few lines of glue:

    A new algorithm built from three tested pieces
    def is_divisible(number, divisor):
    return number % divisor == 0
    def find_max(numbers):
    biggest = numbers[0]
    for n in numbers:
    if n > biggest:
    biggest = n
    return biggest
    def average(numbers):
    return sum(numbers) / len(numbers)
    tickets = [14, 27, 33, 40, 51, 62, 75]
    winners = []
    for ticket in tickets:
    if is_divisible(ticket, 3):
    winners.append(ticket)
    print("Winning tickets:", winners)
    print("Largest:", find_max(winners))
    print("Average:", average(winners))
    Output
    Winning tickets: [27, 33, 51, 75]
    Largest: 75
    Average: 46.5

    Building on correct algorithms pays off in three ways. Development is faster, because you are not rewriting what already works. There is less to test, because the pieces were tested before. And errors are easier to find: if the raffle report is wrong, the bug is most likely in the new glue code, not in find_max.

    Check your understanding

    What is displayed when this code segment runs?

    result ← 4 + 18 MOD 5 * 2
    DISPLAY(result)
    Answers

    What is displayed when this code segment runs?

    a ← 3
    b ← 8
    a ← b
    b ← a
    DISPLAY(a)
    DISPLAY(b)
    Answers

    Which of the following statements assign the same value to winner as the code segment, for every possible value of points and lives? Select two answers.

    Select 2 answers.

    IF(points ≥ 50)
    {
    IF(lives > 0)
    {
    winner ← true
    }
    ELSE
    {
    winner ← false
    }
    }
    ELSE
    {
    winner ← false
    }
    Answers

    The two code segments look alike. For which starting value of n do they display different values?

    Segment I
    IF(n MOD 2 = 0)
    {
    n ← n / 2
    }
    IF(n MOD 2 = 1)
    {
    n ← 3 * n + 1
    }
    DISPLAY(n)
     
    Segment II
    IF(n MOD 2 = 0)
    {
    n ← n / 2
    }
    ELSE
    {
    n ← 3 * n + 1
    }
    DISPLAY(n)
    Answers

    A student creates a new algorithm by combining two procedures that the class wrote and tested earlier. Which of the following are benefits of this approach? Select two answers.

    Select 2 answers.

    Answers

    What is displayed when this code segment runs?

    total ← 0
    n ← 1
    REPEAT 6 TIMES
    {
    IF(n MOD 3 = 0)
    {
    total ← total + n
    }
    n ← n + 1
    }
    DISPLAY(total)
    Answers

    Which of the following is NOT an expression?

    Answers

    Practice exercises

    Fix the Average
    Fix the Bug

    This function should return the average of three test scores, but it gives wrong answers: average_of_three(80, 90, 100) returns about 203.3 instead of 90.0.

    Find the order-of-operations bug and fix it.

    def average_of_three(a, b, c):
    return a + b + c / 3
    Leap Year Checker
    Write Code

    A year is a leap year when it is divisible by 4. Years divisible by 100 are the exception: they are not leap years, unless they are also divisible by 400.

    Write is_leap_year(year) that returns True or False. Combine divisibility checks (with %) and Boolean operators.

    is_leap_year(2024)  ->  True
    is_leap_year(1900)  ->  False
    def is_leap_year(year):
    # Use % to test divisibility by 4, 100 and 400
    pass
    Largest Multiple
    Write Code

    Write max_multiple(numbers, divisor) that returns the largest number in the list that is evenly divisible by divisor. If no number in the list is divisible, return None.

    Build it from two algorithms you know: the divisibility check and finding a maximum.

    max_multiple([4, 9, 12, 7], 3)  ->  12
    max_multiple([5, 11, 13], 2)    ->  None
    def max_multiple(numbers, divisor):
    # Return the largest number divisible by divisor, or None
    pass

    Exam topics: 3.3 Mathematical Expressions (AAP-2.A, AAP-2.B, AAP-2.C), 3.9 Developing Algorithms (AAP-2.L, AAP-2.M)

    Related Tutorials

    Was this tutorial helpful?