COMPUTER ENGINEER

← BlogDATA STRUCTURES / ALGORITHMSPython example pack ↓

Problems, mathematics and analysis · 05 / 53

Amortised, expected and measured cost

Understand → trace → implement → justify

3 min reading · allow extra time for practice

Amortised analysis distributes total sequence cost across operations without requiring probability. A doubling dynamic array occasionally copies n elements, but all copies over n appends sum geometrically to O(n). A single append is worst-case O(n) and amortised O(1).

Aggregate analysis counts the total; accounting assigns credits; the potential method uses stored work Φ. Expected analysis instead specifies a probability distribution. Random-pivot quicksort expectations concern algorithmic randomness, not an unsupported assumption about typical users.

Benchmarks should record distributions, warm-up, repetitions, median/p95, memory and environment. Cache effects and constants matter within the same asymptotic class. Amortised O(1) does not guarantee low latency for every service request; resizing spikes can matter.

Where potential accumulates in a dynamic array

When a full array doubles capacity, copied old elements form 1+2+4+… . The last term is smaller than n and their sum is below 2n. Together with n writes for new elements, n appends take linear work. One append can still cost Θ(n); amortised cost per append is Θ(1). This is not an average over random inputs.

Accounting charges a few credits per append: one for the current write and others toward future resizing. Potential analysis formalises this as Φ(state). Amortised cost equals actual cost plus ΔΦ. Initial potential must be controlled and potential must stay bounded below over the sequence.

Shrinking needs hysteresis. Resizing at half-full in both directions can let alternating insert/delete operations thrash. Growing when full and shrinking at quarter-full allows cheap operations to accumulate between resizes. Amortised guarantees do not bound the latency of an individual request.

Foundation: Trace resizing across eight appends. Intermediate: Derive the geometric copying bound for a 1.5 growth factor. Advanced: Design incremental migration for tighter per-operation latency, stating lookup invariants while two buffers coexist.

A worked trace

Capacities 1,2,4,8,16: sixteen appends copy 1+2+4+8=15 old items, not sixteen copies per operation.

Cost and assumptions

Doubling: O(n) total for n appends. Increasing capacity by one: Θ(n²) total.

Executable example

Run with Python 3. Assertions check a small example; they do not constitute a general correctness proof. Some advanced examples are explicitly small models or reference oracles, not full production implementations.

Python 3
def copied_items(n):
    capacity, size, copied = 1, 0, 0
    for _ in range(n):
        if size == capacity:
            copied += size
            capacity *= 2
        size += 1
    return copied
assert copied_items(16) == 15

TEST YOUR UNDERSTANDING

Your turn

Do expected O(1) hashing and amortised O(1) append have the same justification?

Reveal the reasoning

No. Hashing may depend on hash distribution and load-factor assumptions; append can be bounded deterministically over a sequence.

Take it further

  1. Write down the input contract and the invariant before changing the example.
  2. Add empty, boundary, duplicate and adversarial cases where they are relevant. Compare with a simple oracle.
  3. Derive the cost under the stated model; measure several input sizes and explain discrepancies.

Primary reading & lecture notes

Original explanations in this course; these references provide proofs and further reading. Source links may cover the broader module.

MIT 6.046J · Design and Analysis ↗Pat Morin · Open Data Structures ↗
Solve the exercise before moving on.