Week 06 · Phase 2 · Measuring for real

The Doubling Experiment

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?

Lesson

Double n; compare the work

nn stepsn² steps2ⁿ steps
441616
8864256
161625665,536
Doubling ratioR(n) = T(2n) / T(n)

For linear work the ratio is 2; for quadratic work it is 4. Exponential work has no fixed doubling ratio: 2²ⁿ / 2ⁿ = 2ⁿ.

A ratio gives a candidate power

When T(n) ≈ c nᵖ
  1. T(2n) ≈ c (2n)ᵖ
  2. R ≈ 2ᵖ
  3. p ≈ log₂ R
Observed ratioCandidate model
1constant work
2linear work
4quadratic work
8cubic 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.

Not every curve is a pure power

For T(n) = n log₂ n, n > 1R(n) = 2 + 2 / log₂ n
n1625665,536
R(n)2.502.252.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.

Use a series, not one pair

A useful experiment
  1. choose one input family
  2. n, 2n, 4n, 8n
  3. repeat each measurement
  4. plot + compute ratios
  5. compare with counted work

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.

Practice

Practice questions

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.

Test questions

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:

  1. 10
  2. 32
  3. 100
  4. 512

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.

  1. 4 seconds
  2. 8 seconds
  3. 16 seconds
  4. 64 seconds

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:

  1. log₂ n = Θ(log₁₀ n)
  2. log₂ n dominates log₁₀ n
  3. log₁₀ n dominates log₂ n
  4. they are incomparable

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?

  1. 2
  2. 4
  3. 8
  4. 1/2

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?

  1. 3
  2. 6
  3. 8
  4. 9

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?

  1. constant
  2. linear
  3. quadratic
  4. cubic

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?

  1. constant
  2. linear
  3. quadratic
  4. logarithmic

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)?

  1. 7/6, about 1.17
  2. 2
  3. 4
  4. 1/2

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)?

  1. 2
  2. 3
  3. 4
  4. 8/3

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?

  1. −7 ms
  2. 7 ms
  3. 40 ms
  4. 87 ms

Answer: option B. Subtract the prediction from the observation: 47 − 40 = 7 ms. A positive residual means this run was slower than predicted.

Written questions

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?

Answer & reasoning

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?

Answer & reasoning

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.

Answer & reasoning

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.

Three core tasks

Predict on paper, run the code, then explain any difference. Use the animations below to inspect individual steps.

1. Trace

Times for n = 100, 200, 400 are 2, 8, 32 ms. Compute successive ratios.

Check your reasoning

8/2 = 4 and 32/8 = 4; quadratic is a candidate model.

2. Calculate

If R = 8 for a power law, find p.

Check your reasoning

p = log₂ 8 = 3: a cubic candidate.

3. Change one thing

Add a fixed 100 ms overhead to the three example times. Recompute the ratios.

Check your reasoning

108/102 ≈ 1.059 and 132/108 ≈ 1.222. Fixed overhead hides the growing work at these sizes.

Explore the animations & more worked tasks

6.7Try it yourself

Task 1 — identify by ratio alone

Someone hands you these timings. Name each pattern without seeing the code.

nA (s)B (s)C (s)D (s)
1 0000.00210.00000040.01000.0000060
2 0000.00420.00000040.04010.0000065
4 0000.00840.00000040.16020.0000070
8 0000.01670.00000040.64100.0000075
Animate it — work out every ratio, then name each column
Answers

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.

Task 2 — your own doubling study

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.

Animate it — trace doubling() on all four studies
What to expect

(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.

Task 3 — one figure, two curves

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?

Animate it — watch linear axes turn into log–log
Hint

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.

Task 4 — fit the slope, don't just guess it

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?

Animate it — guess a slope, then let the fit finish
Expected

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.

Task 5 — watch n log n drift

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.

Animate it — watch the ratio column drift towards 2
Expected

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.

Task 6 — a misleading start size

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?

Animate it — see how much of a tiny run is noise
Answer

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).

Check your understanding

6.8Self-check

Doubling n makes the time go from 0.10 s to 0.41 s. The most likely shape is:

A ratio near 4 is the nested-loop signature. Twice the data, four times the work.

Times at n = 1 000 / 2 000 / 4 000 are 0.0000060 / 0.0000065 / 0.0000070 s. This is:

Adding a constant per doubling is exactly what log n does. Multiplying per doubling would be linear or worse.

Your first timing point is 0.00003 s and the ratios are 0.7, 1.9, 0.4. The best next move is:

Ratios only mean something once the signal is well above the timer noise. Grow n first, interpret second.

On a log–log plot your measurements form a straight line of slope 2. Which model is supported over that range?

On log–log axes the power becomes the slope: slope 1 is linear, 2 is quadratic, 3 is cubic, and a flat line is constant.

A sorting benchmark posts ratios 2.20, 2.18, 2.16, 2.15 as n grows. The best reading is:

A slowly drifting ratio above 2 is consistent with n log n. Fitted slopes depend on the interval; derive a general bound from the code rather than treating finite measurements as proof.
Extra material & reference
Optional depth · full technical reference

6.1The question that replaces "is it fast?"

"Is my program fast?" is unanswerable — fast compared to what, on whose machine, with how much data? The useful question is:

The doubling question

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 doublesWhat it meansTypical code shapeName (week 8)
≈ 1.0 (unchanged)Input size is irrelevantA few lines, no loop over the dataO(1)
a fixed tiny addition, not a multipleEach doubling costs one more stepHalving while loopO(log n)
≈ 2.0Twice the data, twice the workOne loop over the dataO(n)
≈ 2.1 – 2.3A loop, plus a little extra per doublingSortingO(n log n)
≈ 4.0Twice the data, four times the workLoop inside a loopO(n²)
≈ 8.0Three nested loopsTriple loopO(n³)

Memorise two rows if you memorise nothing else: 2.0 is fine, 4.0 is a warning.

6.2Running the experiment

A reusable harness. Give it a function and a list of sizes, get a table with ratios.

doubling_experiment.py
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, times

Now try it on three functions with visibly different shapes:

three 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)
n seconds ratio 100000 0.00489 nan 200000 0.00981 2.01 400000 0.01955 1.99 800000 0.03920 2.01 1600000 0.07835 2.00 n seconds ratio 500 0.01254 nan 1000 0.05001 3.99 2000 0.20114 4.02 4000 0.80339 3.99

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.

Watch your starting size

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.

6.3Drawing the picture

A table convinces you; a plot convinces everyone else. Matplotlib is already installed in Colab.

a plot with everything it needs
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.

When the numbers are wildly different

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.

log–log
plt.xscale("log")
plt.yscale("log")

6.4Reading — and measuring — the slope

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:

fit the slope
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}")
linear slope = 1.00 quadratic slope = 2.00

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.

Slope and ratio say the same thing

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.

6.5When the ratio column misleads you

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.

1. The starting size is too small

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.

2. n log n looks like a drifting 2

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:

nratio for n·log₂nreading
1 000 → 2 0002.20above 2, so worse than linear
2 000 → 4 0002.18…
4 000 → 8 0002.17…creeping down…
1 000 000 → 2 000 0002.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.

3. Memory effects can inflate a linear ratio

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.

The habit

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.

6.6Interpreting like a scientist

Every benchmark in this course ends with a short paragraph. Use this skeleton until it becomes automatic:

The five-sentence interpretation
  1. What I measured, and at which sizes.
  2. What the ratio column did (the numbers, not an adjective).
  3. Which growth pattern that matches, and why the code has that shape.
  4. One thing that could have distorted the measurement.
  5. What I would predict for an input ten times larger — a specific number.

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

Predict a larger case, then test it

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.

Learning goals & class plan
By the end of this week you can
  • run a doubling experiment on any function;
  • read the ratio column and identify plausible growth models, then check them against the code;
  • draw a labelled plot of time against input size with matplotlib;
  • use a log–log plot when the numbers span several orders of magnitude;
  • read a growth pattern off the slope of a log–log line, and fit that slope with one line of code;
  • recognise the situations where the ratio column quietly misleads you;
  • write a short, honest paragraph interpreting your own measurements.

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.

Three-hour interactive studio

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.

Optional extra practice

6.9Optional Studio Extension

Optional practice · no submission or deadline
  1. Create AA_Week06.ipynb with the time_it and doubling harness, plus the loglog_slope helper.
  2. Complete Task 2 (four studies), Task 3 (two figures) and Task 4 (fitted slopes for all four).
  3. Complete Task 5 (watch n log n drift) and Task 6 (the misleading start size), and write one sentence on what each taught you about reading a ratio.
  4. For each of the four studies, write the five-sentence interpretation from §6.6, quoting both the last ratio and the fitted slope.
  5. Add one prediction and test it: pick any study, predict the time at the next doubling before running it, then run it and report the error as a percentage.
Optional reference · Words from this week

6.10Words from this week

TermMeaning in plain words
doubling experimentMeasuring at n, 2n, 4n… and watching the ratio of times.
ratio columnEach time divided by the previous one; some machine factors may cancel, but ratios remain empirical.
log–log plotBoth 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 fittingUsing points together (e.g. numpy.polyfit) to estimate a slope; outliers and model mismatch can bias it.
cache effectData outgrowing fast memory, which can push a linear ratio above 2.
extrapolationUsing a measured pattern to predict a size you have not run.
harnessReusable code that runs and times an experiment for you.
Where this leads

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.