Part 7 · 1 chapters · ~8 min

Recurrences

Recurrence relations from recursive code, solving by unrolling, linear recurrences and Fibonacci growth, the cost of naive recursion computed, memoisation, divide-and-conquer recurrences, the master theorem with merge sort and binary search, and amortised analysis of dynamic arrays.

8

From code to recurrence to growth

code
let calls = 0;
const fib = n => { calls++; return n < 2 ? n : fib(n - 1) + fib(n - 2); };
fib(30); calls                 // 2,692,537

// master theorem, T(n) = a·T(n/b) + f(n), compare f(n) with n^(log_b a):
//   merge sort      a=2, b=2, f=n   → n^1 = f      → O(n log n)
//   binary search   a=1, b=2, f=1   → n^0 = f      → O(log n)
//   naive matrix ×  a=8, b=2, f=n²  → n^3 > f      → O(n³);  Strassen a=7 → O(n^2.81)

// amortised: a dynamic array doubling on overflow copies 1 + 2 + 4 + … + n < 2n elements in total
//   → O(1) amortised per push, though one push occasionally costs O(n)
RECURRENCES: WHY NAIVE RECURSION EXPLODES
function calls to compute fib(30), computed
naive recursion2,692,537 callsmemoised59 callsiterative30 steps
swipe the figure sideways, or tap expand for full screen
1/4
the recurrence
Naive fib makes T(n) = T(n-1) + T(n-2) + 1 calls, which grows like φⁿ (φ ≈ 1.618). Computing fib(30) made 2,692,537 calls.
T(n) = T(n-1)+T(n-2)+12.69 million calls