Quiz 2

03. Complexity Analysis — Recurrence Relations & Master Theorem

1743 words
9 min read
Python Week 1: the first filter for runtime behavior
Visual companion
Python
Type and operator map

Python Week 1: the first filter for runtime behavior

View
Revision summary

What this note is really saying

Short form

# 03. Complexity Analysis — Recurrence Relations & Master Theorem > **What problem does this solve?** While big-O gives us a way to express complexity, we need systematic methods to **derive** the complexity of recursive algorithms.

03. Complexity Analysis — Recurrence Relations & Master Theorem

What problem does this solve? While big-O gives us a way to express complexity, we need systematic methods to derive the complexity of recursive algorithms. The Master Theorem is a recipe that instantly gives us the complexity of any divide-and-conquer recurrence.

1. Recurrence Relations — The Language of Recursive Algorithms

Mental Model

A recurrence relation describes the runtime of a recursive algorithm in terms of itself. It's like a domino chain: to know how long the nth domino takes to fall, you need to know how long the (n-1)th takes, plus the time between falls. Every recursive algorithm follows this pattern:
T(n)=aT(n/b)+f(n)T(n) = a \cdot T(n/b) + f(n)
Where:
  • (a) = number of subproblems
  • (n/b) = size of each subproblem
  • (f(n)) = cost of dividing and combining

Step-by-Step: Writing Recurrences

Example 1: Binary Search
python
# runnable
def binary_search(arr, target, left, right):
    if left > right:           # Base case: T(0) = 1
        return -1
    mid = (left + right) // 2  # O(1) work
    if arr[mid] == target:     # Check found
        return mid
    elif arr[mid] > target:
        return binary_search(arr, target, left, mid - 1)  # 1 subproblem, size n/2
    else:
        return binary_search(arr, target, mid + 1, right)
# Recurrence: T(n) = T(n/2) + 1
Example 2: Merge Sort
python
# runnable
def merge_sort(arr):
    if len(arr) <= 1:         # Base case: T(1) = 1
        return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])   # T(n/2)
    right = merge_sort(arr[mid:])  # T(n/2)
    return merge(left, right)      # O(n) — merging
# Recurrence: T(n) = 2T(n/2) + n
Example 3: Towers of Hanoi
python
# runnable
def hanoi(n, source, target, aux):
    if n == 1:                     # T(1) = 1
        print(f"Move disk 1: {source} -> {target}")
        return
    hanoi(n - 1, source, aux, target)  # T(n-1)
    print(f"Move disk {n}: {source} -> {target}")  # 1 move
    hanoi(n - 1, aux, target, source)  # T(n-1)
# Recurrence: T(n) = 2T(n-1) + 1
Example 4: Quick Sort (Worst Case)
python
# runnable
def quicksort(arr, low, high):
    if low >= high:              # Base case
        return
    pivot = partition(arr, low, high)  # O(n) partition
    if pivot == low:                    # Worst case: pivot at one extreme
        quicksort(arr, low + 1, high)   # T(n-1)
    else:
        quicksort(arr, low, pivot - 1)  # T(n/2)
        quicksort(arr, pivot + 1, high) # T(n/2)
# Best case: T(n) = 2T(n/2) + n → O(n log n)
# Worst case: T(n) = T(n-1) + n → O(n²)

2. Solving Recurrences by Unwinding (Substitution)

Method

  1. Substitute the recurrence into itself repeatedly
  2. Look for the pattern
  3. Stop when you reach the base case
  4. Simplify

Example: Binary Search (T(n) = T(n/2) + 1)

pseudo
Step 1:  T(n) = T(n/2) + 1
Step 2:  T(n) = [T(n/4) + 1] + 1 = T(n/4) + 2
Step 3:  T(n) = [T(n/8) + 1] + 2 = T(n/8) + 3
Step 4:  T(n) = T(n/2^k) + k
Now, when (n/2^k = 1), (k = \log_2 n).
T(n)=T(1)+logn=1+logn=O(logn)T(n) = T(1) + \log n = 1 + \log n = O(\log n)

Example: Merge Sort (T(n) = 2T(n/2) + n)

pseudo
Step 1:  T(n) = 2T(n/2) + n
Step 2:  T(n) = 2[2T(n/4) + n/2] + n = 4T(n/4) + 2n
Step 3:  T(n) = 4[2T(n/8) + n/4] + 2n = 8T(n/8) + 3n
Step 4:  T(n) = 2^k T(n/2^k) + kn
When (n/2^k = 1), (k = \log_2 n), (2^k = n).
T(n)=nT(1)+nlogn=n+nlogn=O(nlogn)T(n) = n \cdot T(1) + n \log n = n + n \log n = O(n \log n)

Example: Towers of Hanoi (T(n) = 2T(n-1) + 1)

pseudo
Step 1:  T(n) = 2T(n-1) + 1
Step 2:  T(n) = 2[2T(n-2) + 1] + 1 = 2^2 T(n-2) + 2 + 1
Step 3:  T(n) = 2^2[2T(n-3) + 1] + 3 = 2^3 T(n-3) + 4 + 2 + 1
Step 4:  T(n) = 2^k T(n-k) + (2^k - 1)
When (n - k = 1), (k = n - 1).
T(n)=2n1T(1)+(2n11)=2n1+2n11=2n1=O(2n)T(n) = 2^{n-1} \cdot T(1) + (2^{n-1} - 1) = 2^{n-1} + 2^{n-1} - 1 = 2^n - 1 = O(2^n)

3. The Master Theorem

What It Is

The Master Theorem gives an instant solution for recurrences of the form:
T(n)=aT(n/b)+f(n)T(n) = a \cdot T(n/b) + f(n)
where (a \ge 1), (b > 1), and (f(n) > 0).

The Three Cases

Compute (n^{\log_b a}) (the critical exponent). Compare (f(n)) with it. Case 1: (f(n) = O(n^{\log_b a - \varepsilon})) for some (\varepsilon > 0) → (T(n) = \Theta(n^{\log_b a})) (Leaves dominate — more work is done in the subproblems) Case 2: (f(n) = \Theta(n^{\log_b a} \log^k n)) for some (k \ge 0) → (T(n) = \Theta(n^{\log_b a} \log^{k+1} n)) (Equal work at each level) Case 3: (f(n) = \Omega(n^{\log_b a + \varepsilon})) for some (\varepsilon > 0) → (T(n) = \Theta(f(n))) (Root dominates — more work is done at the top level)

Step-by-Step Examples

Example 1: Binary Search (T(n) = T(n/2) + 1)
ParameterValue
a1
b2
(\log_b a)(\log_2 1 = 0)
(n^{\log_b a})(n^0 = 1)
f(n)1 = (\Theta(1) = \Theta(n^{\log_b a}))
Case 2 applies (k = 0): (T(n) = \Theta(n^0 \log n) = \Theta(\log n))

Example 2: Merge Sort (T(n) = 2T(n/2) + n)
ParameterValue
a2
b2
(\log_b a)(\log_2 2 = 1)
(n^{\log_b a})(n^1 = n)
f(n)n = (\Theta(n) = \Theta(n^{\log_b a}))
Case 2 applies (k = 0): (T(n) = \Theta(n \log n))

Example 3: Tree Traversal (T(n) = 2T(n/2) + 1)
ParameterValue
a2
b2
(\log_b a)1
(n^{\log_b a})n
f(n)1 = (O(n^{1-\varepsilon})) for (\varepsilon = 0.5)
Case 1 applies: (T(n) = \Theta(n)) (This is traversing a binary tree: each node visited once, O(n)).

Example 4: 4-way Merge Sort (T(n) = 4T(n/2) + n)
ParameterValue
a4
b2
(\log_b a)(\log_2 4 = 2)
(n^{\log_b a})(n^2)
f(n)n = (O(n^{2-\varepsilon})) for (\varepsilon = 0.5)
Case 1 applies: (T(n) = \Theta(n^2)) (4 subproblems of half size is more work than the linear merge at each level).

Example 5: Quick Select (Average) (T(n) = T(n/2) + n)
ParameterValue
a1
b2
(\log_b a)0
(n^{\log_b a})1
f(n)n = (\Omega(n^{0+\varepsilon})) for (\varepsilon = 0.5)
Case 3 applies: (T(n) = \Theta(n)) (But this is average case; worst case is T(n) = T(n-1) + n = O(n²)).

4. Recursion Tree Method

Mental Model

Draw a tree where each node is a recursive call, and label each node with the non-recursive work done at that level. Sum across levels. Example: (T(n) = 3T(n/4) + n^2) (Diagram)
LevelNodesWork per nodeTotal work
01(n^2)(n^2)
13((n/4)^2)(3(n/4)^2 = \frac{3}{16}n^2)
29((n/16)^2)(9(n/16)^2 = \frac{9}{256}n^2)
k(3^k)((n/4^k)^2)((\frac{3}{16})^k n^2)
Total: (T(n) = n^2 \sum_{k=0}^{\log_4 n} (\frac{3}{16})^k) Since (3/16 < 1), this is a decreasing geometric series, so the root dominates:
T(n)=Θ(n2)T(n) = \Theta(n^2)

Three Patterns of Recursion Trees

PatternGrowthExampleTotal
DecreasingEach level is a constant fraction less(T(n) = 2T(n/8) + n)(O(n))
EqualEach level does same total work(T(n) = 2T(n/2) + n)(O(n \log n))
IncreasingEach level does more total work(T(n) = 4T(n/2) + n)(O(n^2))

5. Quick Reference: Common Recurrences and Their Solutions

RecurrenceAlgorithmSolutionCase
(T(n) = T(n/2) + 1)Binary Search(\Theta(\log n))Master 2
(T(n) = 2T(n/2) + 1)Tree Traversal(\Theta(n))Master 1
(T(n) = 2T(n/2) + n)Merge Sort(\Theta(n \log n))Master 2
(T(n) = 4T(n/2) + n)4-way Merge(\Theta(n^2))Master 1
(T(n) = T(n/2) + n)Quick Select (avg)(\Theta(n))Master 3
(T(n) = 3T(n/2) + n)Karatsuba(\Theta(n^{\log_2 3}) \approx \Theta(n^{1.58}))Master 1
(T(n) = T(n-1) + 1)Linear Recursion(\Theta(n))Unwinding
(T(n) = T(n-1) + n)Worst Quick Sort(\Theta(n^2))Unwinding
(T(n) = 2T(n-1) + 1)Towers of Hanoi(\Theta(2^n))Unwinding

Practice Questions

Q1. Solve using the Master Theorem: (T(n) = 9T(n/3) + n) Q2. Solve using the Master Theorem: (T(n) = T(2n/3) + 1) Q3. Draw the recursion tree for (T(n) = 2T(n/2) + n^2) and solve it. Q4. Which case of the Master Theorem applies to (T(n) = 8T(n/2) + n^3)? Q5. Solve by unwinding: (T(n) = 3T(n-1) + 1) Q6. A recurrence (T(n) = 2T(n/4) + n^{0.5}). Which Master Theorem case? Q7. What recurrence does Quick Sort have in the best case? What about the worst case? Q8. Show that (T(n) = T(n-1) + 2) is (O(n)). Q9. A divide-and-conquer algorithm divides the problem into 7 subproblems each of size (n/3), and takes (O(n^2)) to combine. What is its complexity? Q10. Prove that (T(n) = 2T(n/2 + 17) + n) is still (O(n \log n)). (Hint: the "+17" doesn't affect asymptotic behavior for large n.)
Answers
A1. a=9, b=3, (\log_b a = \log_3 9 = 2), (n^{\log_b a} = n^2). f(n) = n = (O(n^{2-\varepsilon})) for (\varepsilon = 1). Case 1: (T(n) = \Theta(n^2)).
A2. a=1, b=3/2, (\log_b a = \log_{1.5} 1 = 0), (n^0 = 1). f(n) = 1 = (\Theta(1) = \Theta(n^0)). Case 2 (k=0): (T(n) = \Theta(\log n)).
A3. Root: (n^2). Level 1: (2 \cdot (n/2)^2 = n^2/2). Level 2: (4 \cdot (n/4)^2 = n^2/4). Total = (n^2(1 + 1/2 + 1/4 + ...) = 2n^2 = \Theta(n^2)).
A4. a=8, b=2, (\log_b a = \log_2 8 = 3), (n^3). f(n) = n^3 = (\Theta(n^3)). Case 2 (k=0): (T(n) = \Theta(n^3 \log n)).
A5. Unwinding: (T(n) = 3^k T(n-k) + (3^k - 1)/2). When n-k = 1, k = n-1. (T(n) = 3^{n-1} \cdot 1 + (3^{n-1} - 1)/2 = O(3^n)).
A6. a=2, b=4, (\log_b a = \log_4 2 = 0.5), (n^{0.5}). f(n) = n^{0.5} = (\Theta(n^{0.5})). Case 2 (k=0): (T(n) = \Theta(n^{0.5} \log n)).
A7. Best case: (T(n) = 2T(n/2) + n = O(n \log n)). Worst case: (T(n) = T(n-1) + n = O(n^2)).
A8. (T(n) = T(n-1) + 2 = T(n-2) + 4 = ... = T(1) + 2(n-1) = O(n)).
A9. a=7, b=3, (\log_b a = \log_3 7 \approx 1.77). (n^{\log_b a} \approx n^{1.77}). f(n) = n^2. Compare: n^2 vs n^{1.77}. Since n^2 grows faster, (f(n) = \Omega(n^{\log_b a + \varepsilon})), Case 3: (T(n) = \Theta(n^2)).
A10. For n > threshold, (n/2 + 17 \le 0.6n) (say). So (T(n) \le 2T(0.6n) + n). Master Theorem: a=2, b=1/0.6 ≈ 1.67, (\log_b a = \log_{1.67} 2 \approx 1.36). f(n) = n is dominated by (n^{1.36}), so Case 1: (T(n) = O(n^{1.36})). Actually, the "+17" just shifts the base case — the divide step still splits roughly in half for large n, so the recurrence is essentially (2T(n/2) + n = O(n \log n)). Join Discord Previous02. Algorithm Analysis & Big-O NotationNext04. Searching Algorithms — Linear Search & Binary Search
Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.