Mistakes Under Time Pressure
20 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Under time pressure the same few mistakes appear again and again. Knowing them in advance, and having a quick way to check, saves more marks than a clever trick.
Write the slow answer and compare
A stress test runs a slow correct solution and a fast one on many small random inputs and compares the answers. Any difference gives a tiny failing case that is easy to debug.
Under time pressure a clever solution can be wrong in a way you cannot see. Keep a slow, obviously correct version and run both on many small random inputs.
import random
rng = random.Random(7)for _ in range(500): nums = [rng.randint(-5, 5) for _ in range(rng.randint(3, 8))] k = rng.randint(1, len(nums)) if slow(nums, k) != fast(nums, k): print("differ", nums, k) breakelse: print("all equal")Example 1 · A stress test that passes
import random
def slow(nums, k): return max(sum(nums[i:i + k]) for i in range(len(nums) - k + 1))
def fast(nums, k): window = sum(nums[:k]) best = window for i in range(k, len(nums)): window += nums[i] - nums[i - k] best = max(best, window) return best
rng = random.Random(7)for _ in range(500): nums = [rng.randint(-5, 5) for _ in range(rng.randint(3, 8))] k = rng.randint(1, len(nums)) if slow(nums, k) != fast(nums, k): print("differ", nums, k) breakelse: print("all equal")all equal
What this code does
- slow tries every window by brute force and fast slides one.
- Seeding the generator makes the run repeatable, so a failure can be reproduced.
The else after a for runs only when the loop was not stopped by break. Here it means no input made the two versions differ.
Edge cases to try before you submit
An edge case is an input at the border of what a problem allows, such as an empty list, one item, all equal values or the largest size. Most wrong answers fail on an edge case.
Most wrong answers fail on the border of the input, not on the typical case. Try an empty list, one item, all equal values, all negative values and the largest k.
def best_run(nums, k): if not nums or not 1 <= k <= len(nums): return None window = sum(nums[:k]) best = window for i in range(k, len(nums)): window += nums[i] - nums[i - k] best = max(best, window) return best
print(best_run([-4, -2, -9], 2))print(best_run([], 1))Example 2 · A stress test that finds a bug
import random
def slow(nums, k): return max(sum(nums[i:i + k]) for i in range(len(nums) - k + 1))
def buggy(nums, k): window = sum(nums[:k]) best = 0 for i in range(k, len(nums)): window += nums[i] - nums[i - k] best = max(best, window) return best
rng = random.Random(7)for _ in range(500): nums = [rng.randint(-5, 5) for _ in range(rng.randint(3, 8))] k = rng.randint(1, len(nums)) if slow(nums, k) != buggy(nums, k): print("differ", nums, k, slow(nums, k), buggy(nums, k)) breakdiffer [-3, 1, 5, -5, -4] 5 -6 0
What this code does
- The bug starts best at 0 and never counts the first window, so it fails on small lists where the first window is the best.
- A random test found a short failing case in a few steps, which is far easier to debug than a big one.
Starting best at the first window, not at 0, keeps an all-negative list correct. The guard handles an empty list and an impossible k.
Python traps in recursion and loops
Aliasing means two names or two list slots refer to the same object. Changing it through one name changes what the other shows, which is why a shared path list gives empty or repeated results.
Backtracking appends a list that you keep changing. The result then holds the same list many times, and every entry ends up empty.
Example 3 · Appending a copy of the path
results = []path = []
def walk(i): if i == 2: results.append(path[:]) return for x in (1, 2): path.append(x) walk(i + 1) path.pop()
walk(0)print(results)[[1, 1], [1, 2], [2, 1], [2, 2]]
What this code does
path[:]makes a new list at the moment of the append, so later changes to path do not touch the saved result.- The
popafter the recursive call undoes the choice so the next choice starts clean.
Example 4 · Recursion depth and an explicit loop
def depth(n): return 0 if n == 0 else 1 + depth(n - 1)
try: print(depth(5000))except RecursionError: print("RecursionError")
n = 5000count = 0while n: count += 1 n -= 1print(count)RecursionError 5000
What this code does
- Python stops recursion at about 1000 nested calls, so a depth of 5000 raises RecursionError.
- A loop, or a list used as a stack, has no such limit. Use it when the input can be long and deep.
Example 5 · Python traps in recursion and loops
results = []path = []
def walk(i): if i == 2: results.append(path) return for x in (1, 2): path.append(x) walk(i + 1) path.pop()
walk(0)print(results)[[], [], [], []]
What this code does
- All four entries are the same list object, which is empty at the end.
- Append a copy such as
path[:]orlist(path)instead.
Next step
Check your understanding
10 quick questions