Question 1 · easy
lg 1024 equals:
Answer: option A. 2¹⁰ = 1024, so 1024 can be halved ten times.
Double n, look at the time: flat, twice as slow, or four times as slow?
What does the running time do when the input doubles?
| n | n steps | n² steps | 2ⁿ steps |
|---|---|---|---|
| 4 | 4 | 16 | 16 |
| 8 | 8 | 64 | 256 |
| 16 | 16 | 256 | 65,536 |
For linear work the ratio is 2; for quadratic work it is 4. Exponential work has no fixed doubling ratio: 2²ⁿ / 2ⁿ = 2ⁿ.
| Observed ratio | Candidate model |
|---|---|
| 1 | constant work |
| 2 | linear work |
| 4 | quadratic work |
| 8 | cubic work |
On a log–log plot, log T = log c + p log n: a power law becomes a straight line with slope p. Both axes use logs. A straight line on ordinary axes means something different.
| n | 16 | 256 | 65,536 |
|---|---|---|---|
| R(n) | 2.50 | 2.25 | 2.125 |
A ratio slowly approaching 2 can belong to n log n, not just n. Small inputs can hide the growing term: for T(n) = 1000 + n, R(n) is near 1 when n is small.
Keep units consistent and timings above the noise floor. A single ratio can be distorted by interruptions, caching or memory pressure. Report a candidate growth model and the input range supporting it; establish the model by counting work.
10 test questions · 3 written questions · 13 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
lg 1024 equals:
Answer: option A. 2¹⁰ = 1024, so 1024 can be halved ten times.
Question 2 · medium
Assume time is approximately c·n² with the same c and negligible overhead. A run at n = 1,000 takes 1 second. Estimate the time at n = 4,000.
Answer: option C. The size ratio is 4, so the model predicts 4² × 1 = 16 seconds. A Θ(n²) classification alone does not determine a finite-size timing ratio.
Question 3 · medium
The relationship between log₂ n and log₁₀ n is:
Answer: option A. They differ by the constant factor log₂ 10 ≈ 3.32.
Question 4 · medium · course question
Under a proportional linear model T(n) = c*n, doubling n changes the time by which factor?
Answer: option A. T(2n)/T(n) = c·2n/(c·n) = 2. This prediction assumes the same constant c.
Question 5 · medium · course question
Under T(n) = c*n², tripling n changes the time by which factor?
Answer: option D. T(3n) = c·9n² = 9T(n). Square the input-size ratio.
Question 6 · medium · course question
Measurements at n = 100, 200, 400 are 3, 6, 12 ms. Which simple model best matches these ratios?
Answer: option B. Each doubling of n doubles the measured time. This supports a linear model over the tested range, without proving a universal bound.
Question 7 · medium · course question
Measurements at n = 100, 200, 400 are 2, 8, 32 ms. Which simple model best matches these ratios?
Answer: option C. Each doubling multiplies time by four, matching a proportional n² model.
Question 8 · medium · course question
If T(n) = 5 + 0.01*n milliseconds, what is T(200)/T(100)?
Answer: option A. T(100)=6 ms and T(200)=7 ms. The fixed 5 ms overhead prevents an exact factor of two, even though the variable part is linear.
Question 9 · medium · course question
For T(n) = c*n*log₂(n), what is T(16)/T(8)?
Answer: option D. The ratio is (16 × 4)/(8 × 3) = 64/24 = 8/3. A doubling does not give exactly twice the time for this model.
Question 10 · medium · course question
An ideal n² model predicts 40 ms, but the measured value is 47 ms. If residual means measured minus predicted, what is the residual?
Answer: option B. Subtract the prediction from the observation: 47 − 40 = 7 ms. A positive residual means this run was slower than predicted.
Read each question together with its explanation, trace or proof. Numbering continues from the test questions.
Question 11 · easy
How many times can you halve 64 before reaching 1? Roughly what is lg 1,000,000?
6 (64 → 32 → 16 → 8 → 4 → 2 → 1), and about 20, since 2²⁰ ≈ 1,050,000.
Question 12 · medium
A program takes 1 second at n = 1,000. Assuming its time is approximately proportional to n², n log₂ n, or 2ⁿ over the range being considered, estimate the time at n = 10,000. What assumption makes the estimate possible?
Quadratic: (10,000/1,000)² = 100 seconds. n log n: 10 × log₂(10,000)/log₂(1,000) = 40/3 ≈ 13.3 seconds. Exponential: 2⁹⁰⁰⁰ seconds under the same idealised model. These extrapolations assume the same leading constant and negligible lower-order effects; a Θ bound alone does not determine finite-size timings. Memory, caching and input-dependent work can invalidate the prediction.
Question 13 · medium
Show that log₂ n = Θ(log₁₀ n), and say what this means for Big-O.
by the change-of-base rule, log₂ n = log₁₀ n / log₁₀ 2 ≈ 3.32 × log₁₀ n. A constant factor is invisible to Big-O, so O(log₂ n) = O(log₁₀ n) = O(log n), and we drop the base.
Predict on paper, run the code, then explain any difference. Use the animations below to inspect individual steps.
Times for n = 100, 200, 400 are 2, 8, 32 ms. Compute successive ratios.
8/2 = 4 and 32/8 = 4; quadratic is a candidate model.
If R = 8 for a power law, find p.
p = log₂ 8 = 3: a cubic candidate.
Add a fixed 100 ms overhead to the three example times. Recompute the ratios.
108/102 ≈ 1.059 and 132/108 ≈ 1.222. Fixed overhead hides the growing work at these sizes.
Someone hands you these timings. Name each pattern without seeing the code.
| n | A (s) | B (s) | C (s) | D (s) |
|---|---|---|---|---|
| 1 000 | 0.0021 | 0.0000004 | 0.0100 | 0.0000060 |
| 2 000 | 0.0042 | 0.0000004 | 0.0401 | 0.0000065 |
| 4 000 | 0.0084 | 0.0000004 | 0.1602 | 0.0000070 |
| 8 000 | 0.0167 | 0.0000004 | 0.6410 | 0.0000075 |
A doubles each time — one loop over the data (linear). B never changes — constant. C quadruples — nested loops (quadratic). D grows by a small fixed amount each doubling, not a multiple — that is the halving pattern, logarithmic.
Run doubling() on: (a) summing a list, (b) the worst-case
contains search from week 5, (c) the duplicate-finder from week 4, and
(d) sorted(data) on a shuffled list. Record all four ratio columns.
(a) and (b) ≈ 2.0; (c) ≈ 4.0; (d) ≈ 2.1–2.2. That last one is the fingerprint of n log n — clearly worse than a plain loop, dramatically better than nested loops. Week 13 explains where it comes from.
Plot (b) and (c) from Task 2 on the same axes with labels, title and legend. Then make a second version with log–log axes. Which version communicates better, and to whom?
The linear-axis plot shows the practical horror (one curve shoots off the top). The log–log plot shows the structure (two straight lines with different slopes). Reports for decision-makers usually want the first; technical appendices want the second.
Write the loglog_slope helper from §6.4 and run it on all four studies
from Task 2. Line the fitted slopes up against the last ratio in each column. Do slope
and ratio agree (slope 1 ↔ ratio 2, slope 2 ↔ ratio 4)? Where they disagree, which do
you trust more, and why?
If the intended models dominate, (a) and (b) should fit near 1.0 and (c) near 2.0. For (d), the fitted slope depends on the interval, input ordering and implementation. Outliers can affect a fit. Compare raw samples, ratios and the code-derived model rather than automatically trusting one statistic.
Run doubling(sorted_wrapper, start_n=10000, doublings=7) where
sorted_wrapper(n) shuffles a list of n items and sorts it. This wrapper
measures preparation plus sorting; label that scope. To isolate sorting, adapt the
harness to prepare equivalent shuffled lists outside each timed interval. Read the ratio
column top to bottom. Does it settle on a single number, or drift? Explain what you see
using §6.5, and give the fitted slope.
For the ideal n log n model, the ratio is 2 + 2/log₂ n and approaches 2 from above. Actual ratios and fitted slopes may differ because the wrapper includes other work and sorting adapts to its input. State whether your observations support the model; neither a particular slope nor a crossover is guaranteed.
Run your linear study twice: once with start_n=10 and once with
start_n=1000000, four doublings each. Compare the two ratio columns. Which
one gives clean 2.0s, and what does the other one teach you about trusting a ratio?
The tiny start gives a jumpy, meaningless column (0.7, 1.8, 1.1…) because every run is buried in noise; the large start gives steady 2.0s. Same algorithm, same code — only the size changed. A ratio is only worth reading once the run is well clear of the noise floor (§6.5, case 1).
Doubling n makes the time go from 0.10 s to 0.41 s. The most likely shape is:
Times at n = 1 000 / 2 000 / 4 000 are 0.0000060 / 0.0000065 / 0.0000070 s. This is:
Your first timing point is 0.00003 s and the ratios are 0.7, 1.9, 0.4. The best next move is:
On a log–log plot your measurements form a straight line of slope 2. Which model is supported over that range?
A sorting benchmark posts ratios 2.20, 2.18, 2.16, 2.15 as n grows. The best reading is:
"Is my program fast?" is unanswerable — fast compared to what, on whose machine, with how much data? The useful question is:
If I double the input, what happens to the time?
This one question has a small number of possible answers, and each answer identifies a family of algorithms. You do not need mathematics to use it. You need a stopwatch, four input sizes, and a division.
| Time ratio when n doubles | What it means | Typical code shape | Name (week 8) |
|---|---|---|---|
| ≈ 1.0 (unchanged) | Input size is irrelevant | A few lines, no loop over the data | O(1) |
| a fixed tiny addition, not a multiple | Each doubling costs one more step | Halving while loop | O(log n) |
| ≈ 2.0 | Twice the data, twice the work | One loop over the data | O(n) |
| ≈ 2.1 – 2.3 | A loop, plus a little extra per doubling | Sorting | O(n log n) |
| ≈ 4.0 | Twice the data, four times the work | Loop inside a loop | O(n²) |
| ≈ 8.0 | Three nested loops | Triple loop | O(n³) |
Memorise two rows if you memorise nothing else: 2.0 is fine, 4.0 is a warning.
A reusable harness. Give it a function and a list of sizes, get a table with ratios.
import time
def time_it(func, arg, repeats=5):
best = None
for _ in range(repeats):
start = time.perf_counter()
func(arg)
elapsed = time.perf_counter() - start
if best is None or elapsed < best:
best = elapsed
return best
def doubling(func, start_n=10000, doublings=5):
"""Time func at n, 2n, 4n ... and print the ratio each time."""
sizes, times = [], []
n = start_n
previous = None
print(f"{'n':>9} {'seconds':>12} {'ratio':>7}")
for _ in range(doublings):
t = time_it(func, n)
ratio = t / previous if previous else float('nan')
print(f"{n:>9} {t:>12.5f} {ratio:>7.2f}")
sizes.append(n); times.append(t)
previous = t
n = n * 2
return sizes, timesNow try it on three functions with visibly different shapes:
def constant(n): # ignores n entirely
return n * (n + 1) // 2
def linear(n): # one loop over n
total = 0
for i in range(n):
total += i
return total
def quadratic(n): # loop inside a loop
count = 0
for i in range(n):
for j in range(n):
count += 1
return count
sizes_l, times_l = doubling(linear, start_n=100000)
sizes_q, times_q = doubling(quadratic, start_n=500, doublings=4)You did not need to read the code to tell them apart. The ratio column did it: 2.0 versus 4.0. This is the core practical skill of the course, and you now have it.
Notice that the quadratic experiment starts at n = 500, not 100 000 — a quadratic function at 100 000 would run for hours. Choosing a starting size that finishes but is still big enough to beat the noise is part of the craft. Aim for a first run of roughly 0.01–0.1 seconds.
How do you pick that starting size without guessing? Run the function once at a small
n, look at the time, and scale up. If your shape is linear and n = 1 000
took 0.00001 s, then 0.05 s needs about 5 000 times more work, so start near
n = 5 000 000. If the shape is quadratic, remember that the time grows with the
square: a 100× longer run only needs a 10× bigger n. A quick pilot run saves you
from either a wall of noise or a coffee break.
A table convinces you; a plot convinces everyone else. Matplotlib is already installed in Colab.
import matplotlib.pyplot as plt
plt.figure(figsize=(6, 4))
plt.plot(sizes_l, times_l, marker="o", label="one loop")
plt.plot(sizes_q, times_q, marker="s", label="loop inside a loop")
plt.xlabel("input size n")
plt.ylabel("time (seconds)")
plt.title("Running time vs input size")
plt.legend()
plt.grid(alpha=0.3)
plt.show()Every plot you hand in must have: axis labels with units, a title, and a legend if there is more than one line. A plot without labels is not evidence, it is decoration.
If one curve is a million times taller than another, the small one flattens into the axis. Switch both axes to logarithmic scale and each straight line's slope tells you the growth: slope 1 is linear, slope 2 is quadratic, flat is constant.
plt.xscale("log")
plt.yscale("log")Why does a log–log plot turn a power law into a straight line? The following algebra applies to a fixed constant times n to a fixed power. Other models, including n log n, need not become exactly straight:
T = c · nk
Take the logarithm of both sides and the multiplication becomes addition, the power becomes a multiplier:
log T = log c + k · log n
That is the equation of a straight line — log n along the bottom,
log T up the side, a slope of exactly k, and the constant
c only shifting the line up or down without tilting it. So on log–log axes
the power becomes the slope, and the machine-dependent constant becomes
an offset you can ignore. Constant → slope 0 (flat). Linear → slope 1. Quadratic →
slope 2. Cubic → slope 3.
You do not have to eyeball the slope. numpy.polyfit fits the best straight
line through your logged points and hands you the slope as a number:
import numpy as np
def loglog_slope(sizes, times):
"""Best-fit slope through the points on log-log axes."""
slope, intercept = np.polyfit(np.log(sizes), np.log(times), 1)
return slope
print(f"linear slope = {loglog_slope(sizes_l, times_l):.2f}")
print(f"quadratic slope = {loglog_slope(sizes_q, times_q):.2f}")This is the ratio column's more grown-up cousin. The ratio reads growth from two points at a time; a fitted slope summarizes all your points. Outliers and systematic effects can still bias it. Inspect the samples and residual pattern as well as the slope; a straight-line fit is evidence about the measured range, not a complexity proof.
For the model T = c nk, a slope of k means doubling n multiplies time by
2k. Slope 1 → ratio 2. Slope 2 → ratio 4. Slope 3 → ratio 8.
Slope 0 → ratio 1. They are two readings of one truth: the ratio is local and quick,
the fitted slope summarizes a range and is still sensitive to measurement conditions.
The ratio is a wonderful tool, but a tool you trust blindly will eventually cut you. Three situations make honest ratios lie about the pattern.
Below the noise floor the ratio is meaningless — it wanders 0.6, 1.9, 0.4 with no shape at all, because you are timing interruptions, not your algorithm. This is the number one cause of a "weird" ratio column, and the fix is always the same: grow the starting size until the first run clears roughly 0.01–0.1 seconds, then read again.
The most subtle case. A sorting run does not give a clean 2.0 or 4.0 — it gives a ratio just above 2 that slowly drifts downward towards 2 as n grows:
| n | ratio for n·log₂n | reading |
|---|---|---|
| 1 000 → 2 000 | 2.20 | above 2, so worse than linear |
| 2 000 → 4 000 | 2.18 | … |
| 4 000 → 8 000 | 2.17 | …creeping down… |
| 1 000 000 → 2 000 000 | 2.10 | …towards 2, but never reaching it |
The reason: doubling n doubles the loop (that is the 2), and the log n
factor adds only a sliver more each time — a sliver that shrinks as n grows, because the
logarithm grows ever more slowly. Read too high a starting size and you might call it
linear; read too low and you might call it quadratic. The honest verdict is "just above
2 and falling slowly", which is consistent with an n log n model. A fitted slope
(§6.4) depends on the input range and is not a fixed class identifier. For exact
T(n) = n log₂ n, T(2n)/T(n) = 2 + 2/log₂ n.
A perfectly linear loop can show ratios above 2 once the data grows too big to sit in the CPU's fast cache. Suddenly each item costs a slow trip to main memory, and the time per item rises just as n rises. The algorithm did not change class; the hardware changed gear. If a clean O(n) function starts posting 2.3, 2.6 at very large sizes, suspect the cache before you rewrite the algorithm.
When a ratio surprises you, do not immediately believe it. Ask the three questions in order: is the run long enough, could this be n log n drifting, and has the data outgrown the cache? A fitted slope can summarize the evidence, but inspect the model and samples before deciding.
Every benchmark in this course ends with a short paragraph. Use this skeleton until it becomes automatic:
Worked example, for the quadratic run above:
"I timed the nested-loop counter at n = 500, 1 000, 2 000 and 4 000, five repeats each, taking the fastest. The time ratios were 3.99, 4.02 and 3.99, and a log–log fit gave a slope of 2.00. Four times the work for twice the data matches a loop inside a loop, which is exactly what the code does: the inner loop runs n times for each of the n outer passes. The 4 000 run took nearly a second, so background load on the shared Colab machine could have inflated it slightly. Extrapolating, n = 40 000 would be 100× the 4 000 time — about 80 seconds — which I would not want to run in a live report."
From the beginner notes · Lectures 2, 3
If time is approximately proportional to n², doubling n predicts four times the time. If it is proportional to n, doubling predicts twice the time. These are model predictions to test against fresh observations.
Suppose 1,000 records take one second. At 10,000 records, a quadratic model predicts 100 seconds. An n log₂ n model predicts about 13.3 seconds. The same starting measurement supports very different forecasts until you identify the growth pattern.
Big-Theta describes the growth pattern, but does not give an exact timing ratio for particular input sizes. These estimates assume the same speed per unit of work and smaller terms that matter little. Record the difference between the predicted and measured time (the residual)—and investigate when the model stops fitting.
Engineering use. Before increasing a sensor log tenfold, predict processing time and memory use. Test a smaller increase first.
Reading the timing examples: displayed seconds and fitted slopes are illustrative output, not measurements from your machine. Run the experiment, retain the actual samples, and report the input and measurement conditions. Exact operation-count formulas are separate mathematical claims.
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_Week06.ipynb with the time_it and doubling harness, plus the loglog_slope helper.| Term | Meaning in plain words |
|---|---|
| doubling experiment | Measuring at n, 2n, 4n… and watching the ratio of times. |
| ratio column | Each time divided by the previous one; some machine factors may cancel, but ratios remain empirical. |
| log–log plot | Both axes logarithmic; exact power laws become straight lines, while other models may curve. |
| slope (log–log) | The power in T = c·nᵏ; slope 1 is linear, 2 is quadratic, 0 is constant. |
| line fitting | Using points together (e.g. numpy.polyfit) to estimate a slope; outliers and model mismatch can bias it. |
| cache effect | Data outgrowing fast memory, which can push a linear ratio above 2. |
| extrapolation | Using a measured pattern to predict a size you have not run. |
| harness | Reusable code that runs and times an experiment for you. |
The ratio column revealed the shape, but the seconds drift with your laptop. Week 7 switches to counting steps — a measure that does not change when the hardware does.