Exit slides Esc

Unit 3 — What Does This Program Do? (Branching)

Slide 1 of 67

  1. The problem: What is output?

    Here is a question of the kind you meet on every ACSL contest. It is written in ACSL pseudocode, the made-up programming language ACSL uses so that every student reads the same program, whatever language they code in.

    INPUT a, b
    c = a % 7
    IF c > 3 && b < a THEN
       d = int(a / 4) + b
    ELSE IF !(b > 10) THEN
       d = a - b ^ 2
    ELSE
       d = a + b
    END IF
    OUTPUT d
  2. What is output when the input is a = 26 and b = 9?

    Some of it looks like Python and some of it does not: &&, !, ^, END IF, and an int that does not quite behave like Python's. The contest gives you no computer for this part: you read the program, trace it by hand, and write one exact answer.

  3. This unit teaches you to do exactly that, in three steps: how to read ACSL pseudocode and turn it into Python (Lesson 1), how to trace IF branches without slipping (Lesson 2), and, for Intermediate and Senior students, how to trace loops, arrays, grids and strings, which their Contest 1 also tests (Lesson 3). Write down your first guess now. In Lesson 1 you will translate the program into Python and check your guess; in Lesson 2 you will learn to get the answer by hand, the way the contest needs.

    • Translate ACSL pseudocode into Python with the dialect table: /, ^, !, &&, ||, abs and sqrt.

    • Use math.floor for ACSL's int, which always rounds down, even below zero.

    • Trace branches with a trace table: IF … ELSE IF chains, separate IFs, nested decisions, and && before ||.

    • Intermediate and above: trace FOR and WHILE loops, arrays, grids and strings, which your Contest 1 also tests.

  4. Lesson 1 — Reading ACSL pseudocode

    Division: Junior and above.

    An ACSL program is a list of statements, one per line. The ones you meet most:

    • INPUT a, b reads values. In a short-answer question the values are given to you, for example "the input is a = 26 and b = 9".

    • x = expression stores a value, exactly as in Python.

    • OUTPUT x prints. OUTPUT x, y prints both values on one line; in this book, as in Python's print(x, y), they are separated by one space.

    • IF … THEN … ELSE IF … THEN … ELSE … END IF makes decisions. END IF marks where the IF stops, the job indentation does in Python.

    • WHILE … END WHILE and FOR … NEXT repeat. They are in Lesson 3.

  5. The dialect table

    Python and ACSL pseudocode agree on most things. This table lists the ones you must translate, and the traps.

    ACSL pseudocode Python Watch out
    =, ==, !=, <, <=, >, >= the same = stores; == compares
    +, -, *, % the same % is the remainder
    / / real division: 7 / 2 is 3.5. In Python / always gives a decimal (a float), so 6 / 3 prints 2.0
    ^ (sometimes ↑) ** 2 ^ 5 is 32: multiply 2 by itself five times
    ! not ACSL's ! binds before everything else; in Python write not (…) with brackets
    && and true only when both sides are true
    abs(x) abs(x) the size of x without its sign
    int(x) math.floor(x) the greatest integer <= x. Not Python's int(x), which chops toward zero: ACSL int(-1.5) is -2, Python int(-1.5) is -1
    sqrt(x) math.sqrt(x) gives a float: math.sqrt(49) is 7.0. Questions use perfect squares or say how to round
    IF … ELSE IF … ELSE … END IF if … elif … else the first true branch runs, and only that one
    FOR k = 1 TO 9 STEP 2 … NEXT k for k in range(1, 10, 2) ACSL includes the end value (Lesson 3)
    A(3), A(r, c) A[3], A[r][c] arrays and 2D arrays (Lesson 3)
    len(S), S[j], S[a:b] len(S), S[j], … ACSL substrings are not Python slices (Lesson 3)

    One more pair: ACSL's || is Python's or, which is true when at least one side is true.

    ACSL works out an expression in this order: ! first, then ^, then *, / and %, then + and -, then the comparisons, then &&, and || last. Python's order is the same, except that not comes after the comparisons. When you translate, add brackets to make the order you mean certain.

  6. Real division and the remainder

    Python has three division tools. ACSL pseudocode has only / and %; a whole-number quotient is written int(a / b). Predict each line, then check it against the output.

  7. print(7 / 2)
    print(6 / 3)
    print(17 % 5)
    print(17 // 5)

    Output

    3.5
    2.0
    2
    3
    

    6 / 3 prints 2.0, not 2. Python's / always gives a float, even when the division comes out even. When a question shows a Python program, write exactly what Python prints, 2.0 included.

  8. Powers

    ACSL writes a power with ^; Python writes it with **. A power is worked out before *, /, + and -.

  9. print(2 ** 5)
    print(10 - 3 ** 2)
    print((10 - 3) ** 2)

    Output

    32
    1
    49
    

    10 - 3 ** 2 is 10 - 9, which is 1: the power goes first. Brackets change the order, so (10 - 3) ** 2 is 49.

  10. ACSL int is a floor

    ACSL's int(x) gives the greatest integer that is less than or equal to x. For 3.7 that is 3, and for -3.7 it is -4, because -4 is below -3.7 on the number line and -3 is above it.

    Python's int(x) does something else: it chops off the decimal part, which moves a negative number up toward zero. To get ACSL's int, use floor from Python's math module. You met import with the random module; import math loads the math tools the same way.

  11. import math
    
    print(math.floor(3.7), int(3.7))
    print(math.floor(-3.7), int(-3.7))
    print(math.floor(-7 / 2))

    Output

    3 3
    -4 -3
    -4
    

    For positive numbers the two agree. For negative numbers they do not: math.floor(-3.7) is -4, but int(-3.7) is -3. So ACSL int(-3.7) is -4, and translating it with Python's int gives a wrong answer. Always translate ACSL int(x) as math.floor(x).

  12. Absolute value and square roots

    abs(x) works the same in both languages. ACSL's sqrt(x) is math.sqrt(x) in Python, and it always gives a float.

  13. import math
    
    print(abs(-8), abs(8))
    print(math.sqrt(49))
    print(math.sqrt(50))
    print(math.floor(math.sqrt(50)))

    Output

    8 8
    7.0
    7.0710678118654755
    7
    

    math.sqrt(49) is 7.0, a float. The square root of 50 is not a whole number, so a question that uses it must say how to round; int(sqrt(50)) in ACSL, which is math.floor(math.sqrt(50)) in Python, is 7.

  14. Not, and, or

    ACSL's !, && and || are Python's not, and and or. Put brackets after not, so it applies to the whole comparison.

  15. b = 9
    print(not (b > 10))
    print(b > 5 and b < 8)
    print(b > 5 or b < 8)

    Output

    True
    False
    True
    

    b > 10 is False, so not (b > 10) is True. and needs both sides true; or needs just one.

  16. Translating the opening problem

    Here is the problem from the top of the unit, translated line by line. Here the input values are written into the program, as a = 26 and b = 9, in place of reading them with input().

  17. import math
    
    a = 26
    b = 9
    c = a % 7
    if c > 3 and b < a:
        d = math.floor(a / 4) + b
    elif not (b > 10):
        d = a - b ** 2
    else:
        d = a + b
    print(d)

    Output

    15
    
  18. Each piece of the table appears once: && became and, ! became not, ^ became **, ELSE IF became elif, and ACSL's int became math.floor. Running the translation is how you check an answer while you practise. In the contest you have no computer, so Lesson 2 is about getting the same answer by hand.

  19. The full program, reading a and b from one input line, is saved as assets/l1.py.

  20. Try it
    import math
    
    parts = input().split()
    a = int(parts[0])
    b = int(parts[1])
    c = a % 7
    if c > 3 and b < a:
        d = math.floor(a / 4) + b
    elif not (b > 10):
        d = a - b ** 2
    else:
        d = a + b
    print(d)
  21. Run it from this unit's folder, feeding it an input file:

    python assets/l1.py < assets/l1/1.in

    The files assets/l1/2.in to assets/l1/4.in send the program down each of the other branches. Trace each one by hand first, then check with the program.

  22. Lesson 2 — Tracing branches

    Division: Junior and above.

    To trace a program is to run it by hand, the way the computer would. Keep a small table with one column per variable. Go line by line; whenever a variable changes, write its new value in its column. At an IF, work out the condition, write down true or false, and follow only the branch that runs.

  23. Tracing the opening problem

    Input: a = 26, b = 9.

    line a b c d note
    c = a % 7 26 9 5 26 = 3 × 7 + 5
    IF c > 3 && b < a 5 > 3 true, 9 < 26 true, so true
    d = int(a / 4) + b 15 26 / 4 = 6.5, int gives 6, and 6 + 9 = 15

    The first condition is true, so the ELSE IF and ELSE branches are skipped entirely. The answer is 15. Check it against assets/l1/1.out.

  24. Only the first true branch runs

    In an IF … ELSE IF … ELSE chain the conditions are tested from the top. The first true one runs its branch, and the rest are never looked at, even if they are true too. Predict the output, then check it.

  25. temp = 31
    if temp > 20:
        label = "warm"
    elif temp > 30:
        label = "hot"
    else:
        label = "cold"
    print(label)

    Output

    warm
    

    31 > 30 is true, but that line is never reached, because 31 > 20 was true first. The output is warm. Contest questions love this trap: when you trace a chain, stop at the first true condition.

  26. Separate IFs all get tested

    Three separate IF statements are not a chain. Each one is tested in turn, and each test sees the values left by the ones before it.

  27. x = 7
    if x > 5:
        x = x - 4
    if x < 5:
        x = x * 3
    if x % 2 == 0:
        x = x + 1
    print(x)

    Output

    9
    

    x goes 7 → 3 → 9, and 9 is odd, so the last IF does nothing: the output is 9. Had these been one IF … ELSE IF chain, only the first branch would run and the output would be 3. Look for END IF (or, in Python, elif) to tell the two shapes apart.

  28. Nested conditions

    An IF can sit inside a branch of another IF. The inner test only happens if the program reaches it. In ACSL pseudocode, each IF has its own END IF, which tells you where it stops.

    IF x > y THEN
       IF x % 2 == 0 THEN
          z = x - y
       ELSE
          z = x + y
       END IF
    ELSE
       z = y * 2
    END IF
  29. x = 9
    y = 4
    if x > y:
        if x % 2 == 0:
            z = x - y
        else:
            z = x + y
    else:
        z = y * 2
    print(z)

    Output

    13
    

    Trace from the outside in: 9 > 4 is true, so go inside; 9 % 2 is 1, so the inner ELSE runs and z is 13. The outer ELSE is never reached.

  30. And before or

    In both languages, && (and) is worked out before || (or), just as * comes before +.

  31. x = 8
    y = 2
    print(x > 5 or y > 5 and x < y)
    print((x > 5 or y > 5) and x < y)

    Output

    True
    False
    

    The first line is x > 5 or (y > 5 and x < y), which is True or False, so True. The brackets in the second line force the or first: True and False, so False. When a condition mixes && and || without brackets, put in the brackets yourself before tracing.

  32. Whole-number arithmetic

    Many branching questions pull digits apart with % and whole-number division. In ACSL that division is int(n / 10); in Python, n // 10 gives the same whole number.

  33. n = 482
    hundreds = n // 100
    tens = n // 10 % 10
    ones = n % 10
    print(hundreds, tens, ones)
    if hundreds + ones > tens:
        print("outside wins")
    else:
        print("middle wins")

    Output

    4 8 2
    middle wins
    

    n // 10 % 10 is 48 % 10, which is 8, the tens digit. The remainder n % 10 is always the last digit, and n // 10 removes it.

  34. Put it together: this program reads three whole numbers, the side lengths of a triangle, and prints what kind of triangle they make. It uses a chain, a nested IF, and and/or together. It is saved as assets/l2.py.

  35. Try it
    parts = input().split()
    a = int(parts[0])
    b = int(parts[1])
    c = int(parts[2])
    if a + b <= c or a + c <= b or b + c <= a:
        print("NOT A TRIANGLE")
    elif a == b and b == c:
        print("EQUILATERAL")
    elif a == b or b == c or a == c:
        print("ISOSCELES")
    else:
        if a * a + b * b == c * c or a * a + c * c == b * b or b * b + c * c == a * a:
            print("RIGHT SCALENE")
        else:
            print("SCALENE")
  36. Trace it by hand on each input before you run it from this unit's folder:

    python assets/l2.py < assets/l2/1.in
  37. The order of the chain matters. Three equal sides are also "two equal sides", so the EQUILATERAL test must come before the ISOSCELES test; and the "not a triangle" test comes first of all. Inputs assets/l2/1.in to assets/l2/5.in reach every one of the five outputs.

  38. Lesson 3 — Loops, arrays, grids and strings

    Division: Intermediate and above.

    Junior students meet loops, arrays and strings in later contests. Intermediate and Senior students are tested on all of them in Contest 1, so this lesson covers each one in ACSL pseudocode and in Python. Tracing works the same way: a table of variables, one line at a time.

  39. FOR loops include the end

    FOR k = 1 TO 9 STEP 2
       OUTPUT k
    NEXT k

    This outputs 1, 3, 5, 7, 9: ACSL's FOR includes the end value 9. Python's range stops before its end, so the translation is range(1, 10, 2): one more than the end. Without STEP, the step is 1.

  40. for k in range(1, 10, 2):
        print(k)

    Output

    1
    3
    5
    7
    9
    
  41. A negative STEP counts down. FOR k = 20 TO 2 STEP -6 visits 20, 14, 8, 2. Counting down, Python's range needs an end one less than ACSL's, so the translation is range(20, 1, -6).

  42. total = 0
    for k in range(20, 1, -6):
        total = total + k
    print(total)

    Output

    44
    

    The rule is "go one step past the ACSL end": add 1 when counting up, subtract 1 when counting down. To trace a stepped loop, first write out the list of values the counter takes (20, 14, 8, 2), then trace the body once for each.

  43. WHILE loops

    WHILE condition … END WHILE is Python's while. The test comes before each pass, so the body can run zero times.

  44. n = 3517
    reverse = 0
    while n > 0:
        reverse = reverse * 10 + n % 10
        n = n // 10
    print(reverse)

    Output

    7153
    

    A trace table makes this easy: n goes 3517, 351, 35, 3, 0 and reverse goes 0, 7, 71, 715, 7153. When n reaches 0 the test fails and the loop ends.

  45. Arrays

    An ACSL array is written with round brackets: A(3) is the item at position 3. A program says where its positions start. Most past ACSL problems start at A(1); some start at A(0).

    Python lists always start at position 0. When an ACSL array starts at 1, the easy translation is to put an unused 0 at position 0, so that A(1) is A[1].

  46. A(1) = 6, A(2) = 3, A(3) = 8, A(4) = 5
    FOR j = 1 TO 3
       IF A(j) > A(j + 1) THEN
          A(j + 1) = A(j + 1) + A(j)
       END IF
    NEXT j
    OUTPUT A(4)
  47. A = [0, 6, 3, 8, 5]
    for j in range(1, 4):
        if A[j] > A[j + 1]:
            A[j + 1] = A[j + 1] + A[j]
    print(A[4])

    Output

    22
    

    Trace the array as a row of boxes and change one box at a time. j = 1: 6 > 3, so A(2) becomes 9. j = 2: now 9 > 8, so A(3) becomes 17. j = 3: 17 > 5, so A(4) becomes 22. Each pass sees the boxes as the pass before left them.

  48. 2D arrays: grids

    A 2D array is a grid of rows and columns, like a spreadsheet or a game board. ACSL writes one box as A(r, c): row r, column c, always in that order.

    In Python a grid is a list of lists: a list of rows, each row a list. grid[r] is a whole row, and grid[r][c] is one box in it.

  49. grid = [[3, 8, 1],
            [7, 2, 9]]
    print(grid[1])
    print(grid[1][2])
    print(len(grid), len(grid[0]))

    Output

    [7, 2, 9]
    9
    2 3
    

    Python counts rows and columns from 0, so grid[1][2] is row 1, column 2: the 9. len(grid) is the number of rows, and len(grid[0]), the length of the first row, is the number of columns.

  50. To visit every box, use nested loops: rows on the outside, columns on the inside. This order, left to right along row 0, then row 1, and so on, is how you read a page.

  51. grid = [[3, 8, 1],
            [7, 2, 9]]
    for r in range(len(grid)):
        for c in range(len(grid[0])):
            print(r, c, grid[r][c])

    Output

    0 0 3
    0 1 8
    0 2 1
    1 0 7
    1 1 2
    1 2 9
    
  52. A program often builds a grid from a rule. Build each row with append, then append the row to the grid. Here each box holds r * c, and, as in most ACSL problems, rows and columns start at 1: row 0 and column 0 are unused zeros.

    FOR r = 1 TO 3
       FOR c = 1 TO 4
          A(r, c) = r * c
       NEXT c
    NEXT r
  53. A = []
    for r in range(4):
        row = []
        for c in range(5):
            row.append(r * c)
        A.append(row)
    print(A[2])
    print(A[3][4], A[2][3])

    Output

    [0, 2, 4, 6, 8]
    12 6
    

    The loops run from 0 so that the unused row 0 and column 0 exist too; after that, A[r][c] in Python is exactly A(r, c) in ACSL. On paper, draw the grid as a table with the row numbers down the side and the column numbers across the top.

  54. Some programs walk the grid a different way: down each column. Swap the loops: columns outside, rows inside. This adds up column 1 of the grid above: 1 + 2 + 3.

  55. A = [[0, 0, 0, 0, 0],
         [0, 1, 2, 3, 4],
         [0, 2, 4, 6, 8],
         [0, 3, 6, 9, 12]]
    total = 0
    for r in range(1, 4):
        total = total + A[r][1]
    print(total)

    Output

    6
    
  56. Strings

    In ACSL, as in Python, a string's positions start at 0, len(S) is its length, S[j] is one character, and + joins strings. Substrings are where the two differ. ACSL's rules, for S = "ACSL WDTPD":

    ACSL means gives Python
    S[:3] the first 3 characters "ACS" S[:3]
    S[4:] the last 4 characters "DTPD" S[len(S) - 4:]
    S[2:6] positions 2 through 6, both included "SL WD" S[2:7]
    S[0] position 0 "A" S[0]

    A Python slice S[a:b] stops before position b, and S[a:] starts at position a. So only the first form is written the same way. When a Python program is shown, use Python's rules; when ACSL pseudocode is shown, use ACSL's.

  57. S = "ACSL WDTPD"
    print(S[:3])
    print(S[len(S) - 4:])
    print(S[2:7])
    print(S[4:])

    Output

    ACS
    DTPD
    SL WD
     WDTPD
    

    The last line is Python's own S[4:]: everything from position 4 on, " WDTPD", starting with the space. ACSL's S[4:] would be "DTPD". Python programs in questions may also use the string methods you know, such as upper, lower and replace.

  58. Real division inside a loop

    A real division can change a whole number into a decimal, and ACSL's int then rounds down, even below zero.

  59. import math
    
    x = 5
    for k in range(3):
        x = x - 4
        print(x / 2, math.floor(x / 2))

    Output

    0.5 0
    -1.5 -2
    -3.5 -4
    

    x goes 1, -3, -7. -3 / 2 is -1.5, and ACSL's int(-1.5) is -2, one below, not -1. This is exactly where Python's int would give a wrong answer.

  60. Put it together: this program reads a grid of whole numbers and prints the column, counted from 1, with the largest total, and that total. The first input line holds the numbers of rows and columns; each following line is one row. If two columns tie, the one further left wins. It is saved as assets/l3.py.

  61. Try it
    parts = input().split()
    rows = int(parts[0])
    cols = int(parts[1])
    grid = []
    for r in range(rows):
        row = []
        for piece in input().split():
            row.append(int(piece))
        grid.append(row)
    best_col = 0
    best_total = 0
    for c in range(cols):
        total = 0
        for r in range(rows):
            total = total + grid[r][c]
        if c == 0 or total > best_total:
            best_col = c
            best_total = total
    print(best_col + 1, best_total)
  62. Run it from this unit's folder:

    python assets/l3.py < assets/l3/1.in
  63. The outer loop is over columns, because each total runs down one column. best_total starts from the real first column (c == 0), never from a made-up 0, so an all-negative grid (assets/l3/3.in) still works.

    • ACSL's int(x) is the greatest integer ≤ x: math.floor(-3.7) is -4, but Python's int(-3.7) is -3.

    • In an IF … ELSE IF chain only the first true branch runs; separate IFs are each tested.

    • && is done before ||, so bracket the && parts when you trace.

    • A trace table with one row per step makes the printed line easy to check. The opening program prints 15.

    • Intermediate and above: a FOR loop includes its end value, and ACSL substrings are not Python slices.

  64. You can now trace a branching ACSL program and write its exact output.