BIGO-5

Recurrences

Cost: Big-O and friendsLesson 5 of 815 min

A loop’s cost is a sum. A recursive function’s cost is harder, because it calls itself. Its cost is written as an equation called a recurrence, and this lesson shows two ways to solve one.

Cost described by itself

Here is merge sort in outline. SORT-2 builds it in full.

def merge_sort(a):
    if len(a) <= 1:
        return a
    mid = len(a) // 2
    left = merge_sort(a[:mid])        # T(n/2)
    right = merge_sort(a[mid:])       # T(n/2)
    return merge(left, right)         # about n steps

Let T(n) be the number of steps for a list of n values. The function makes two calls on half the list, and does about n steps of its own (the slices and the merge). So:

The second part, T(1) = 1, is the base case: a list of one value costs a constant amount. A recurrence is not an answer yet. We want a plain formula for T(n).

Unrolling

One way is to replace T by its own definition again and again, and look for a pattern. Take n to be a power of 2, so the halves are always whole:

T(n) = 2T(n/2) + n
     = 2[2T(n/4) + n/2] + n  = 4T(n/4) + 2n
     = 4[2T(n/8) + n/4] + 2n = 8T(n/8) + 3n
     ...
     = 2^k T(n/2^k) + k·n      # 2^k: k twos multiplied together

Here 2^k means 2ᵏ, k twos multiplied together. The halving stops when n/2ᵏ = 1, which is when k = log₂ n. Put that in: T(n) = n × T(1) + n log₂ n = n + n log₂ n. So T(n) is Θ(n log n).

A pattern spotted this way is a guess. To be sure, prove it by induction (CORR-3): assume the formula for n/2, and show it holds for n.

The recursion tree

A picture often makes the pattern obvious. Draw each call as a box, with the calls it makes below it. Write next to each box the work it does itself, not counting its calls. Then add up the tree one level at a time.

FIG BIGO-5.1Adding up a recursion tree, level by level
Recurrence
8
Work at each level
LevelCalls × sizeWork at this level
01 × 88
total so far 8
1/5

T(8) is one call on 8 values. It does 8 steps of its own work (for example, merging), then makes two calls on 4 values each.

Step 1 of 5

The two trees have the same work at the top, n, but very different totals. Halving gives log₂ n levels. Shrinking by one gives n levels. Most of the difference between fast and slow divide-and-conquer comes down to this.

Four recurrences to know

Common recurrences and their solutions
RecurrenceSolutionExample
T(n) = T(n/2) + 1Θ(log n)binary search
T(n) = 2T(n/2) + nΘ(n log n)merge sort
T(n) = T(n − 1) + 1Θ(n)recursive sum of a list
T(n) = T(n − 1) + nΘ(n²)selection sort; quick sort’s worst case
T(n) = 2T(n − 1) + 1Θ(2ⁿ)towers of Hanoi; listing every subset

Lesson 6 gives a shortcut for the first two rows and many more like them.

BIGO-5 counts as completed once you step through the figure to the last step.