Embercode
✕
LoopsLesson 6 of 16

50 min read

Module assignment: Lap timer report in Python

Why this assignment exists

The earlier lessons each taught one loop idea. The for loop counts through a known sequence of values, such as range(5). The while loop repeats while a condition holds. Python has no do-while loop, so while True with a break at the end does that job. break and continue change the path inside a loop, and a return from a function leaves nested loops. Nested loops compare every item with every other item, and their work grows as n squared. In real programs these tools are used together, not one at a time.

This assignment gives you one realistic program that needs all of them. You will write a lap timer report. A runner or a team leader types in the lap times from a training session, and the program prints a short report. Nothing in the program is hard on its own. The skill is in choosing the right loop for each part, and in reading the rules carefully so that the output is exactly right.

Work through the spec below, then the requirements checklist, then the rubric. Only open the hints and the reference solution when you have tried for at least 30 minutes. The reference solution is there so that you can compare your code, not so that you can copy it.

The brief

A running coach records the lap times of one training session. Each lap is recorded as a whole number of seconds. The coach writes the number of laps planned, then the lap times. Sometimes the timer is stopped early. The coach then types the value 0 for that lap, and every value after the 0 must be ignored.

Your program reads the input and prints a five-line report. The report helps the coach see the session at a glance.

Input format

  • Line 1: one whole number n, the number of laps planned. n is from 1 to 1000.
  • Line 2: n whole numbers, the lap times in seconds, separated by spaces. Each lap time is from 1 to 3600. A value of 0 means the timer was stopped. Ignore that 0 and every value after it.

The input may be split across more than one line. Do not rely on the line breaks. A simple way to read every number is sys.stdin.read().split(), which gives a list of strings, one per number. Each string is turned into an integer with int.

Output format

Print exactly five lines, in this order and with these exact labels:

  1. Laps counted: k, where k is the number of laps read before the first 0. If there is no 0, k is n.
  2. Total: T, where T is the sum of the counted lap times.
  3. Fastest: lap L with S seconds, where L is the 1-based position of the fastest counted lap and S is its time. If two laps tie for fastest, report the first one. If no laps were counted, print Fastest: none instead.
  4. Faster than average: c, where c is the number of counted laps whose time is strictly less than the average lap time. The average is T divided by k. Compare without division, by checking whether lap * k is less than T. Division gives a decimal answer, and decimals can be slightly wrong.
  5. Equal pairs: p, where p is the number of pairs of counted laps, with the first position less than the second, whose times are exactly equal.

Every number is printed as a whole number, with no decimal point.

Worked example 1

Input:

5
62 58 58 61 60

Output:

Laps counted: 5
Total: 299
Fastest: lap 2 with 58 seconds
Faster than average: 2
Equal pairs: 1

Check each line. The total is 62 + 58 + 58 + 61 + 60, which is 299. The fastest time is 58, and it first appears at lap 2, so that is the lap reported. The average is 299 divided by 5, which is 59.8. Two laps are below that, the two 58s. The only equal pair is the two 58s, which are laps 2 and 3.

Worked example 2

Input:

6
70 65 0 90 80 60

Output:

Laps counted: 2
Total: 135
Fastest: lap 2 with 65 seconds
Faster than average: 1
Equal pairs: 0

The 0 is the third value, so laps 1 and 2 are counted and everything after it is ignored. The 90, 80 and 60 are not part of the report. The average of 70 and 65 is 67.5, so only 65 is faster. There are no equal pairs.

Worked example 3

Input:

3
0 50 50

Output:

Laps counted: 0
Total: 0
Fastest: none
Faster than average: 0
Equal pairs: 0

The first value is 0, so the timer was stopped before any lap was recorded. Nothing is counted, and the report says so. The program must not crash, divide by zero, or print a fastest lap that does not exist.

Requirements checklist

Copy this list into your own notes, and tick each item when your program meets it. Each item is something a reviewer can check by reading the code or running a test.

  • The program reads all the numbers with sys.stdin.read().split() and turns them into integers with int.
  • Reading uses a while loop that stops after n values or at the first 0, whichever comes first.
  • The stop at 0 is written with break, not with a flag that is checked later.
  • Values after the first 0 are never stored or counted.
  • The total and the count are computed with loops, not by hand. The built-in sum would also give the right answer, but this assignment is practice for the loop.
  • The fastest lap uses a for loop over indexes, and a tie keeps the first lap found.
  • The average comparison multiplies the lap by k and compares with the total. No float or division is used.
  • The equal pairs count uses nested loops, with the inner range starting at the outer index plus one.
  • The program prints the Fastest: none line when no laps were counted, and does not read laps[0] from an empty list.
  • The five lines use the exact labels from the output format, with no extra text.
  • All three worked examples produce the exact output shown.
  • The program passes the edge cases below, and you have tested them yourself.
  • Variable names say what they hold. Comments explain the why of each loop, not the what.
  • Your code includes one short comment giving the time complexity of the pair count, and why it is acceptable for n up to 1000.

Edge cases you must test

Test each of these. The reference solution handles all of them, and a grader will try them.

  • n equal to 1, with a single lap of 45. The output should show one lap, the same fastest lap, and no pairs.
  • A list where every lap has the same time, such as 4 laps of 60. The equal pairs count for four equal values is 6, since there are 4 choose 2 pairs.
  • A 0 as the very first lap. The output must be the empty report shown in worked example 3.
  • A 0 as the last lap. The lap before it is the last counted one, and all earlier values count.
  • A maximum case, n equal to 1000 with 1000 different values. Your program should finish well under one second. On the machine used to check this lesson, the reference solution took about 0.04 seconds for this case, including Python's startup. Your machine will give a different time.

Grading rubric

Your work is graded on correctness first, then on code quality. The levels below describe what each score looks like in practice.

60 percent: the program works for simple input

  • The program runs and prints the right five lines for worked example 1.
  • It reads the lap times and computes the total correctly.
  • It stops at a 0 for simple cases, even if the stop logic is not clean.
  • It may miss the tie rule, the empty case, the average comparison or the equal pairs count. The output may be wrong for those cases.

80 percent: the program is correct for all normal input

  • All three worked examples produce exact output.
  • The fastest lap uses the first lap on a tie.
  • The average comparison is done without a float or division.
  • The equal pairs count uses a nested loop with the inner loop starting after the outer index.
  • The Fastest: none line is printed for the empty case, with no error.
  • The reading loop uses break for the 0 and a count for n.

100 percent: the program is correct, clear and explained

  • Everything in the 80 percent level is correct, and every edge case in the list above is tested.
  • The variables have clear names, and each loop is short enough to read at a glance.
  • A comment explains why the reading loop stops at 0, and why the total needs no special integer type.
  • The complexity comment states that the pair count is O(n squared) with n at most 1000, and explains that about 500000 comparisons is fast enough.
  • The code has no unused variables, no repeated loops that could be one loop, and no magic numbers without a name when a name would help.

Step-by-step hints

Open each hint only when you are stuck on that step. Each hint gives one small push, not the answer.

Hint 1: reading the input

Read all the numbers into a list of strings with sys.stdin.read().split(). The first string is n. Create an empty list laps for the counted values. A while loop can read values while len(laps) is less than n. Inside it, convert the next string with int. If the value is 0, use break. Otherwise append it to laps.

Hint 2: the total and the count

After the reading loop, len(laps) is the number of counted laps. Use a for loop over laps to add the values into a total that starts at 0. Print the count and the total as the first two lines.

Hint 3: the fastest lap

Start with best at index 0. Loop over the indexes from 1 up to k minus 1. If the lap at i is strictly less than the lap at best, set best to i. Using strictly less than is what keeps the first lap on a tie. Print best + 1, because the output uses 1-based positions. Check for k equal to 0 before you read laps[0].

Hint 4: the average comparison

Count the laps where lap * k is less than total. This is the same as asking whether the lap is less than total divided by k, but it uses whole numbers only. Make sure the count starts at zero and goes up inside the loop.

Hint 5: the equal pairs

Use two nested loops. The outer loop uses i from 0 up to k minus 1. The inner loop uses j from i + 1 up to k minus 1. Each pair (i, j) is then checked once. Add one to the count when the two laps are equal.

Reference solution

Try the program yourself first. The solution below is one correct version. Your version may use different names and layout and still be correct. Compare the output on every worked example and every edge case, not only the code.

import sys
 
tokens = sys.stdin.read().split()
n = int(tokens[0])
 
# Read laps until n values are read or a 0 stops the timer.
# A list grows as values are added, so its length is the count k.
laps = []
position = 1
while len(laps) < n:
    lap = int(tokens[position])
    position += 1
    if lap == 0:
        break
    laps.append(lap)
 
k = len(laps)
 
total = 0
for lap in laps:
    total += lap
 
print(f"Laps counted: {k}")
print(f"Total: {total}")
 
if k == 0:
    print("Fastest: none")
else:
    best = 0
    for i in range(1, k):
        if laps[i] < laps[best]:
            best = i
    print(f"Fastest: lap {best + 1} with {laps[best]} seconds")
 
# lap * k < total is the same as lap < average, without division.
faster = 0
for lap in laps:
    if lap * k < total:
        faster += 1
print(f"Faster than average: {faster}")
 
# The pair count is O(k squared). With k at most 1000 that is
# about 500000 comparisons, which is fast enough.
pairs = 0
for i in range(k):
    for j in range(i + 1, k):
        if laps[i] == laps[j]:
            pairs += 1
print(f"Equal pairs: {pairs}")

Why the reference solution is built this way

Each part of the solution uses the loop that fits its job, and the choice is deliberate.

The reading loop is a while because it has two stop rules: the count reaching n, and a 0 appearing. A for loop would make the second rule awkward, because you would need a flag or a loop variable that is changed in the body. The break is the clearest way to say "stop now" when the 0 is read, and it leaves the rest of the input unread, which is fine here.

The reading uses a list that grows, so there is no need to know the size in advance. laps.append adds a value at the end, and len(laps) is the count. This is different from a fixed-size array, where you would keep a separate counter.

The total is a plain int. Python integers have no fixed size, so the sum cannot overflow. The largest total is 1000 laps of 3600 seconds, which is 3.6 million. A fixed-size integer type would also hold that, but Python does not need the extra step of choosing a wider type. You should be able to explain why no special type is needed.

The fastest search uses a for loop over indexes from 1 and a strict less-than comparison. Starting at index 1 is correct because index 0 is already the first candidate. The strict comparison keeps the first lap on a tie. A <= would report the last tied lap, which breaks the rule in the spec.

The average comparison avoids division on purpose. A lap is faster than the average when lap is less than total divided by k. Multiplying both sides by k gives lap * k less than total, and that is a whole-number comparison with no rounding. Floating point numbers can make a value that is exactly equal look slightly different. In Python, 0.1 + 0.2 == 0.3 is False, because 0.1 + 0.2 is stored as 0.30000000000000004. Avoiding floats is the safe choice for this kind of test.

The pair count uses nested loops. The inner loop starts at i + 1, so each pair is counted once and no lap is paired with itself. The count is O(k squared). For k up to 1000, the inner body runs about 500000 times, which is well within the time limit. A faster method exists, using a count table or sorting, and it is a later topic. For this assignment, the simple nested loop is correct and clear.

Testing your program

Test in three steps. First, run the three worked examples exactly as written, and compare the output line by line. A difference of one space is a failure, so check the spacing as well as the words. To feed a file to the program, run it as python3 main.py < input.txt, where input.txt holds the example input.

Second, run each edge case in the list above. Predict the output by hand before you run it. Where your prediction and the output differ, find out why before you change the code.

Third, try your own inputs. Choose a case that you think might break your program, such as a tie for the fastest lap, a 0 in the middle, or an n of 1000. Predict the output, run it, and compare.

If your program is correct, you should be able to state the answer to any input before you run it. That is the real test of understanding a loop.

Stretch ideas (optional, not graded)

If you finish early, try one of these. None of them changes the grade.

  • Print the lap number of the slowest lap as well as the fastest. Decide what should happen on a tie.
  • Print the average to two decimal places using total / k and an f-string such as f"{total / k:.2f}". Explain why this makes the average comparison less safe than the whole-number version.
  • Rewrite the reading loop as while True, with the check for 0 at the end of the loop body, so the program reads at least one lap before it checks for the 0. Explain what changes for an input whose first value is 0.
  • Move the pair loops into a function, and stop the count after 10 pairs with a return. Explain why this changes the report.

What this module covered

  • The for loop over range for a known count, traced by hand, with its start, stop and off-by-one rules.
  • The while loop for a condition, with a count and a sentinel value.
  • The while True loop with break for work that must happen before the check.
  • break and continue, the early exit from a loop, and return from a function to leave nested loops.
  • Nested loops, the counting of their work, and the classic problems of factorial, prime tests, Fibonacci, GCD, bubble sort, selection sort and insertion sort.
Practice this topic: coding problems for Loops →

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.

← Nested loops and classic problems in Python