Question 1 · easy
Two nested loops each run n times around a constant-time body. Which is the tightest listed upper bound as n grows?
Answer: option C. Nested loops multiply.
for, range, and while — plus counting how many steps your program really takes.
How do I make the computer repeat work, and how do I count that work?
x = 7
if x % 2 == 0:
print("even")
else:
print("odd")== compares values. if / elif / else chooses the first matching branch. Indentation shows which instructions belong to it.
total = 0
for i in range(1, 5):
total += i
print(total)total += i means total = total + i. range(n) gives 0 through n − 1. break leaves a loop; continue skips to its next turn.
n = 3
for i in range(n):
for j in range(n):
print(i, j)Two separate loops of n turns do n + n = 2n actions. Nesting makes every outer turn repeat the whole inner loop.
n = 20
steps = 0
while n > 1:
n //= 2
steps += 1
print(steps)For powers of two: 2ᵏ → … → 1 takes exactly k updates.
A while loop must move toward a stopping condition. Using n += 1 here would never reach it.
10 test questions · 4 written questions · 14 total
Each question is followed by its answer and explanation. Hard questions also include a starting hint and smaller reasoning steps.
Choose one option. Cost questions state their model and whether the bound is tight, expected or worst-case. Here lg means log₂, and heap positions start at 1.
Question 1 · easy
Two nested loops each run n times around a constant-time body. Which is the tightest listed upper bound as n grows?
Answer: option C. Nested loops multiply.
Question 2 · medium
The running time of i = 1; while i < n: i = 3i is:
Answer: option A. Tripling reaches n after about log₃ n steps, and the base does not matter.
Question 3 · medium · course question
How many values does range(4) produce?
Answer: option A. They are 0, 1, 2 and 3. The stop value 4 is excluded.
Question 4 · medium · course question
What is the final total after total = 0 and then, for each i in range(1, 4), total = total + i?
Answer: option D. The loop visits 1, 2 and 3, so the total becomes 1, then 3, then 6.
Question 5 · medium · course question
How many times does the body run in: x = 1; while x < 8: x = 2*x?
Answer: option B. The body changes x from 1 to 2, then 4, then 8. At 8 the condition is false, so there are three executions.
Question 6 · medium · course question
An outer loop runs 3 times and its inner loop runs 4 times on every outer iteration. How often does the inner body run?
Answer: option C. Each of the three outer iterations contributes four inner executions: 3 × 4 = 12.
Question 7 · medium · course question
A loop visits i = 0, 1, 2 and runs its inner body i times. What is the total inner-body count?
Answer: option A. Add the row lengths: 0 + 1 + 2 = 3. The bounds start at zero, so this differs from summing 1 + 2 + 3.
Question 8 · medium · course question
What happens in x = 0; while x < 3: print(x), if the body contains no other statement?
Answer: option D. The body never changes x. The condition 0 < 3 remains true after each iteration.
Question 9 · medium · course question
How many values does range(2, 9, 2) produce?
Answer: option B. The values are 2, 4, 6 and 8. The step is 2 and the stop value 9 is excluded.
Question 10 · medium · course question
A loop visits [4, 7, 2, 9] and stops immediately when it finds 2. How many items does it inspect?
Answer: option C. It inspects 4, then 7, then 2. Stopping at the match means 9 is not inspected.
Read each question together with its explanation, trace or proof. Numbering continues from the test questions.
Question 11 · easy
Two nested loops each run n times around a constant-time body. Running time? What if there are three nested loops?
Θ(n²); Θ(n³). Nested loops multiply.
Question 12 · medium
What is the running time of this loop nest? for i = 1 to n: for j = 1 to i: (constant work)
the inner loop runs 1 + 2 + … + n = n(n + 1)/2 times in total, so Θ(n²). Notice that “the inner loop depends on i” does not change the order compared with a full n × n nest.
Question 13 · medium
What is the running time of i = 1; while i < n: i = 2i?
Θ(log n). The counter doubles each time, and doubling 1 until it reaches n takes ⌈lg n⌉ steps.
Question 14 · medium
What is the running time of for i = 1 to n: (j = 1; while j < n: j = 2j)?
Θ(n log n): the outer loop runs n times and each inner loop runs about lg n times.
Predict on paper, run the code, then explain any difference. Use the animations below to inspect individual steps.
For the sum loop, replace range(1, 5) with range(1, 6). List each total.
1, 3, 6, 10, 15: five additions.
Count body actions for two nested loops, each range(8). Compare two separate loops.
Nested: 8 × 8 = 64. Separate: 8 + 8 = 16.
Start the halving loop at 32, then 31. Predict the update counts.
32 → 16 → 8 → 4 → 2 → 1 takes 5. 31 → 15 → 7 → 3 → 1 takes 4.
Print 10, 9, 8 … 1, then "Lift off". Use a loop, not ten print lines.
for i in range(10, 0, -1):
print(i)
print("Lift off")Ten steps for a countdown from 10, a hundred from 100: the work is proportional to where you start.
Count how many numbers between 1 and n are even, and also count how
many steps your program takes. Try n = 100, 200, 400 and note
both numbers.
n = 100
evens = 0
steps = 0
for number in range(1, n + 1):
steps = steps + 1
if number % 2 == 0:
evens = evens + 1
print(f"n = {n}: {evens} evens, {steps} steps")Steps: 100, 200, 400 — exactly n. Answer: 50, 100, 200. Both grow in a straight line, and doubling n doubles both.
Write down, on paper, the step count you expect from the nested loop for
n = 300. Then run it. Then explain any surprise in one sentence.
90 000. If you guessed 600 you added when you should have multiplied — a nested loop multiplies, and that single mistake is behind most "why is my program frozen?" questions.
Modify the while loop from §3.3 so the starting size is a variable. Record the number of integer size reductions for 1 000, 10 000, 100 000, 1 000 000. What pattern do you see?
9, 13, 16, 19. Multiplying the input by ten adds only three or four reductions. These are the counts for floor division until the size is at most one, not target comparisons. Compare that with the nested-loop table above — these two behaviours are as different as walking and teleporting.
Using break, find the first number from 1 upward that is a multiple of
both 7 and 9, and report how many numbers you had to check. Predict the count before
you run it.
steps = 0
answer = None
for k in range(1, 1000):
steps = steps + 1
if k % 7 == 0 and k % 9 == 0:
answer = k
break
print(f"answer {answer}, checked {steps}")
63 is 7 × 9, the first number both divide, so the loop stops after checking 63.
Without break it would have run all the way to 999.
For numbers 1 to 20, print "Fizz" if the number is a multiple of 3, "Buzz" if a multiple of 5, "FizzBuzz" if both, and the number itself otherwise.
for n in range(1, 21):
if n % 3 == 0 and n % 5 == 0:
print("FizzBuzz")
elif n % 3 == 0:
print("Fizz")
elif n % 5 == 0:
print("Buzz")
else:
print(n)The order of the branches is the whole trick: test "both" first, because a multiple of 15 is also a multiple of 3, and Python takes the first true branch. Put the "Fizz" test first and 15 would wrongly print "Fizz".
How many numbers does range(1, 11) produce?
A single loop over n items takes 1 000 steps when n = 1 000. A nested double loop over the same data takes:
You double n from 500 to 1 000. Which program's step count grows the least?
What does break do inside a loop?
break exits the loop then and there. The skip-this-pass keyword is continue.A loop that should total 1 to 5 keeps printing 5. The most likely cause is:
Before repetition, one small tool: doing something only in certain cases. Python checks the condition, and runs the indented block only if it is true.
temperature = 31
if temperature > 30:
print("Hot")
elif temperature > 15:
print("Pleasant")
else:
print("Cold")
Two things trip up beginners: the colon at the end of the line, and the
indentation (four spaces). In Python the indentation is not
decoration — it is how the language knows which lines belong to the if.
| Comparison | Means |
|---|---|
== | is equal to (two signs — one sign is assignment!) |
!= | is not equal to |
> < >= <= | greater / less / at least / at most |
and or not | combine conditions |
The order matters. Python checks the branches top to bottom and takes the
first one that is true, then skips the rest. That is why the "Pleasant" branch
can safely say just > 15: if the temperature had been above 30 we would
already have stopped at "Hot". Read a chain of elifs as "otherwise, if…".
for i in range(5):
print("step", i)
range(5) produces 0, 1, 2, 3, 4 — five values, starting at zero, stopping
before five. Programmers count from zero often enough that you will stop
noticing within a fortnight.
range(5) → 0 1 2 3 4range(1, 6) → 1 2 3 4 5range(0, 10, 2) → 0 2 4 6 8 (step of two)Adding up the numbers from 1 to n is the classic first loop:
n = 100
total = 0
for number in range(1, n + 1):
total = total + number
print(f"The numbers 1 to {n} add up to {total}")The pattern — start an accumulator at zero, add to it inside the loop, use it after — is worth memorising. You will write it a hundred times.
Use for when you know how many repetitions you need, while
when you do not.
remaining = 1000
reductions = 0
while remaining > 1:
remaining = remaining // 2 # halve the size, rounding down
reductions = reductions + 1
print(f"Size left: {remaining}, reductions: {reductions}")This captures the repeated-halving idea from week 1, but counts integer size reductions, not a complete search. The sizes are 1000, 500, 250, 125, 62, 31, 15, 7, 3, 1: nine reductions. Change 1000 to 1 000 000 and the count is 19. The loop does not inspect a target or count a final candidate comparison, so do not equate these exact counts with the guessing game.
If the condition never becomes false, the cell runs forever. In Colab, press the stop button (■) next to the cell. Every programmer does this in their first week; it breaks nothing. The usual cause: you forgot to change, inside the loop, the very thing the condition tests.
Here is the technique that carries the whole course. Add a variable whose only job is to count how many times the interesting line runs.
n = 50
steps = 0
total = 0
for number in range(1, n + 1):
total = total + number
steps = steps + 1 # one unit of work
print(f"n = {n}, result = {total}, steps = {steps}")
Run it with n = 50, then 100, then 200. The step count is always exactly
n. Double the input, double the work: a straight-line relationship. In
week 8 this gets the name O(n), but you can already see it in your own output.
For a loop this small, timing is useless — the numbers bounce around depending on what else your machine is doing. Counting is exact and repeatable. We will add the stopwatch in week 5 and use both together for the rest of the course.
Now the moment that makes people sit up. Put one loop inside another:
n = 5
steps = 0
for i in range(n):
for j in range(n):
steps = steps + 1
print(f"n = {n}, steps = {steps}")
The inner loop runs n times for each pass of the outer loop, so
the total is n × n. Fill in this table by running the code — do not guess:
| n | single loop steps | nested loop steps |
|---|---|---|
| 5 | 5 | 25 |
| 10 | 10 | 100 |
| 100 | 100 | 10 000 |
| 1 000 | 1 000 | 1 000 000 |
| 10 000 | 10 000 | 100 000 000 |
Ten times more input, a hundred times more work. This is why "it worked fine on my test data" and "it froze on the real data" are the same program. Nested loops are not forbidden — sometimes you genuinely must compare every pair — but from now on, when you write one, you should feel a small alarm go off.
Combining a decision with a loop is where loops earn their keep. Two keywords give you
fine control: break leaves the loop immediately, and continue
skips the rest of the current pass and moves on to the next.
Early exit with break. Suppose we want the first number
whose square is over 1000, and we want to know how many we had to check:
steps = 0
answer = None
for k in range(1, 1000):
steps = steps + 1
if k * k > 1000:
answer = k
break # found it — no need to look further
print(f"first is {answer}, checked {steps} numbers")
Without break the loop would grind on to 999 for no reason. Stopping early
is exactly what makes a "best case" cheaper than a "worst case" — a distinction we make
precise when we search lists in week 4.
Skipping with continue. Add up only the numbers from 1 to
20 that are not multiples of 3:
total = 0
skipped = 0
for number in range(1, 21):
if number % 3 == 0:
skipped = skipped + 1
continue # jump straight to the next number
total = total + number
print(f"total {total}, skipped {skipped}")
Notice that continue did not save any work in the big-picture sense — the
loop still visited all 20 numbers, so the step count is still n. It changed
what happens on a pass, not how many passes there are. Keep that
separation clear: skipping an item is not the same as never visiting it.
Four loop bugs account for most of the confusion in these first weeks. Meet them here so you recognise them in your own cells tonight.
| Mistake | Symptom | Fix |
|---|---|---|
Off-by-one in range | Loop stops one short, or runs one too many | Remember range(a, b) stops before b; for 1…n use range(1, n + 1). |
| Resetting inside the loop | Your total or counter is always tiny or wrong | Set the accumulator to 0 above the loop, not inside it. |
| Wrong indentation | A line runs once after the loop instead of every pass (or vice versa) | Lines inside the loop are indented under it; lines after it are not. |
Infinite while | The cell never finishes | Make sure the loop body changes the variable the condition tests. |
The second one is subtle enough to show. Both loops below look reasonable; only one gives the right total for 1 to 5, which is 15:
# WRONG: total is wiped clean on every pass
for number in range(1, 6):
total = 0
total = total + number
print("wrong version:", total) # prints 5, not 15
# RIGHT: total starts at 0 once, then grows
total = 0
for number in range(1, 6):
total = total + number
print("right version:", total) # prints 15In the wrong version, every pass throws the running total away and rebuilds it from zero, so at the end it only holds the last number. The lesson is the one-line habit: initialise accumulators above the loop. When a total or a counter comes out wrong, this is the first place to look.
When a loop confuses you, trace it for a tiny input — say n = 3 — writing
down the value of every variable after each pass. Three rows of a table almost always
reveal the bug, and it is far faster than staring at the whole thing.
From the beginner notes · Lectures 2, 3
Do not count indentation levels alone. Draw one mark for each execution of the operation you care about. If the outer counter runs from 1 to n and the inner counter runs from 1 to that outer counter, the rows contain 1, 2, …, n marks. At n = 4 there are 10 marks, not 16.
The total is n(n + 1)/2. Pair the first and last rows, then the second and second-last: each pair has n + 1 marks. A full rectangular pair of loops has n² marks instead. Both eventually grow quadratically, but their exact counts differ.
A doubling counter follows a different pattern: 1, 2, 4, 8, … . Count the doublings rather than the size of the final value. We will attach the formal growth notation in Week 8.
Engineering use. For a collision check between parts, distinguish pairs counted in both orders, pairs counted only once, and a part paired with itself.
if / elif / else;for loop and a while loop;break and continue;00:00–01:00: launch, predict–run–explain cycle, questions · 01:00–01:10: break · 01:10–02:00: worked variation, peer instruction, questions · 02:00–02:10: break · 02:10–03:00: core mechatronics practice, exam bridge, and exit ticket.
Ask at any point. Weekly self-checks stay private; optional extensions are not collected.
Need a slower explanation? Open the English + Türkçe reference guide.
AA_Week03.ipynb and solve Tasks 1–6.break, and reports how many numbers it checked. Explain the result in one sentence.| Term | Meaning in plain words |
|---|---|
| loop / iteration | Repeating a block of instructions; one pass through it. |
range(a, b) | The numbers from a up to but not including b. |
| accumulator | A variable that collects a running total or count. |
| step counter | An accumulator whose only job is to measure the work done. |
break / continue | Leave the loop now / skip to the next pass. |
| nested loop | A loop inside a loop — the counts multiply, not add. |
| indentation | The four spaces that tell Python which lines belong inside a block. |
These are exercises from Skiena's Algorithm Design Manual, Chapter 2, matched to this week and solved step by step. They are all about the same skill you practised above: counting how many times a loop runs, and turning that count into a tidy formula.
Consider this fragment. The outer loop runs i from 1 to n; for
each i the inner loop runs j from i up to
2*i, printing "foobar" each time. Let T(n) be the total number
of "foobar"s printed. (a) Write T(n) as a summation.
(b) Simplify it to a closed form.
for i in range(1, n + 1): # i = 1, 2, ..., n
for j in range(i, 2 * i + 1): # j = i, i+1, ..., 2i
print("foobar")
Start with one pass of the outer loop. Fix a value of i.
The inner loop runs j from i up to and including 2*i.
How many whole numbers are there from i to 2*i? Count them:
it is (2i − i) + 1 = i + 1. The "+1" is the same off-by-one care from
§3.2 — both ends are included, so you add one. Quick check with i = 3:
j takes 3, 4, 5, 6 — that is four values, and i + 1 = 4. Good.
(a) Add up every pass of the outer loop. The outer loop does this for
i = 1, 2, …, n, so we add i + 1 for each of those:
T(n) = ∑i=1n (i + 1)
(b) Simplify. Split the sum into two easier sums:
T(n) = (1 + 2 + … + n) + (1 + 1 + … + 1)
The first bracket is the famous "add 1 up to n" from §3.2, which equals
n(n+1)/2. The second bracket is just the number 1 added n
times, which is n. So:
T(n) = n(n+1)/2 + n = (n² + n + 2n)/2 = (n² + 3n)/2
The biggest piece is the n², so for large n this grows like
a nested loop should — it is Θ(n²), the two-loops-multiply behaviour from §3.5.
A step counter confirms the formula for small n:
for n in range(1, 6):
steps = 0
for i in range(1, n + 1):
for j in range(i, 2 * i + 1):
steps = steps + 1 # one "foobar"
formula = (n * n + 3 * n) // 2
print(f"n = {n}: counted {steps}, formula {formula}")Answer: (a) T(n) = ∑i=1n (i + 1);
(b) T(n) = n(n+1)/2 + n = (n² + 3n)/2, which is Θ(n²).
Show that the alternating sum
1² − 2² + 3² − 4² + … + (−1)k−1 k²
equals (−1)k−1 · k(k+1)/2. (We will convince
ourselves it is true rather than write a formal proof.)
What the statement says. On the left we add the squares
1, 4, 9, 16, … but flip the sign on every second one: plus, minus, plus, minus.
The claim is that this messy-looking total is always just k(k+1)/2 —
the plain "add 1 up to k" number — carrying a plus sign when k is odd and
a minus sign when k is even (that is all (−1)k−1
does: it is +1 for odd k, −1 for even k).
Check the pattern for the first few k. The quickest way to trust an identity is to compute both sides on small cases and watch them line up:
| k | Left side (the alternating sum) | Right side (−1)k−1·k(k+1)/2 |
|---|---|---|
| 1 | 1 = 1 | + 1·2/2 = 1 |
| 2 | 1 − 4 = −3 | − 2·3/2 = −3 |
| 3 | 1 − 4 + 9 = 6 | + 3·4/2 = 6 |
| 4 | 1 − 4 + 9 − 16 = −10 | − 4·5/2 = −10 |
| 5 | … + 25 = 15 | + 5·6/2 = 15 |
Why it works — pair the terms. Group each minus with the plus just
before it: (1² − 2²), then (3² − 4²),
and so on. Each pair is a difference of squares, and there is a neat fact hiding in it:
(2m−1)² − (2m)² = −(4m − 1) = −(2m−1) − (2m)
In plain words, each plus-minus pair collapses to just the negative sum of the two
numbers involved. So after pairing, the whole alternating sum of squares turns into
a plain running total of 1, 2, 3, … (with a sign) — and a plain running total of
1 up to k is exactly k(k+1)/2. The sign at the end is whatever
the last term carried: plus for odd k, minus for even k. That
is the right-hand side. (If k is odd there is one unpaired plus term left
over at the end, and the arithmetic still lands on the same formula — worth checking
yourself as an optional challenge.)
A four-line check confirms both sides agree all the way to k = 20:
for k in range(1, 21):
left = sum((-1) ** (i - 1) * i * i for i in range(1, k + 1))
right = (-1) ** (k - 1) * k * (k + 1) // 2
print(k, left, right, left == right)Answer: the identity holds:
1² − 2² + … + (−1)k−1 k² = (−1)k−1 · k(k+1)/2,
verified for k = 1…20 and explained by pairing consecutive terms.
In the song "The Twelve Days of Christmas", on the first day my true love sends 1 gift,
on the second day 2 new gifts plus the 1 from before, and so on — each day you
receive that day's number of gifts on top of everything from the earlier days. If
Christmas lasts n days, exactly how many presents arrive in total?
How many arrive on a single day. On day d the true love
delivers 1 + 2 + … + d presents (the first gift, plus the second,
…, up to the d-th). That is the "add 1 up to d" number again, so a
single day d brings d(d+1)/2 presents.
Add up all the days. The grand total over n days is the
sum of each day's haul:
Total(n) = ∑d=1n d(d+1)/2
A small table makes the running total concrete:
| Day n | Presents that day = n(n+1)/2 | Total so far |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 3 | 4 |
| 3 | 6 | 10 |
| 4 | 10 | 20 |
The closed form. Those running totals 1, 4, 10, 20, … are the tetrahedral numbers (imagine stacking triangular layers of cannonballs into a pyramid). The tidy formula for the sum above is:
Total(n) = n(n+1)(n+2)/6
Check it against the table: n = 4 gives 4·5·6/6 = 20.
For the real song, n = 12: 12·13·14/6 = 2184/6 = 364 —
one present for (almost) every day of the year. A tiny program agrees:
for n in (1, 2, 3, 4, 12):
total = 0
for d in range(1, n + 1):
total = total + d * (d + 1) // 2 # presents delivered on day d
formula = n * (n + 1) * (n + 2) // 6
print(f"n = {n}: total {total}, formula {formula}")
Notice the highest power is n³, so the present count grows like
Θ(n³) — even faster than the nested-loop Θ(n²) tables of §3.5.
Answer: Total(n) = ∑d=1n d(d+1)/2 = n(n+1)(n+2)/6,
which for n = 12 is 364 presents. The growth is Θ(n³).
You can repeat work and count the repetitions. Week 4 gives you something worth repeating over — lists — and you will watch the count grow with the data.