Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 5

Mastering Recurrence Relations with the Master Theorem

Recommended resources

Hello! Welcome back.

In our last lesson, we built a foundational toolkit for analyzing recursive algorithms from scratch. We learned to derive recurrence relations and solve them using the visual recursion tree method and the formal substitution method. While powerful, these methods can be time-consuming.

Today, we'll add a powerful tool to your arsenal that acts as a significant shortcut. Our learning outcome is to apply the Master Theorem to analyze divide-and-conquer recurrence relations. This theorem provides a "cookbook" approach for quickly solving a very common class of recurrences, which will be invaluable for saving time and mental energy during a coding interview. True to your preference for first principles, we won't just learn the recipe; we'll also touch upon why it works.

1. The "Sledgehammer" for Divide-and-Conquer

Many divide-and-conquer algorithms, like Merge Sort, have a runtime that can be described by a recurrence of the form:

Let's quickly break down these components.

Master Theorem (Master Method) Introduction
This diagram shows the general form of a recurrence relation suitable for the Master Theorem. `a` is the number of recursive calls (subproblems), `n/b` is the size of each subproblem, and `f(n)` is the cost of the work done outside the recursive calls (dividing the problem and combining the results).

The Master Theorem gives us a way to find the asymptotic bound for T(n) by simply comparing the function f(n) with the term n^{\log_b a}. This comparison tells us whether the runtime is dominated by the cost of the recursive calls (the work done at the "leaves" of the recursion tree) or by the cost of the work done at each step (the work done at the "root" and intermediate nodes).

Let's watch a brief review of the theorem and its three cases.

Problem Session 2 (MIT 6.006 Introduction to Algorithms, Spring 2020)

This video from MIT OpenCourseWare provides a concise and formal review of the Master Theorem. The instructor refers to it as a 'giant sledgehammer' for solving recurrences, which is an apt description. Pay close attention to how he defines the three cases based on the comparison between f(n) and n^{\log_b a}.

Watch from 04:41 to 10:45. This section lays out the three cases of the Master Theorem, which are the core of today's lesson. Don't worry about memorizing the exact formulas on the first pass; focus on the high-level idea of comparing f(n) to n^{\log_b a}.


2. The Three Cases: A Quick Summary

As you saw in the video, the theorem boils down to a competition between two terms: the "recursion cost" represented by n^{\log_b a} and the "combination cost" f(n).

Here’s a summary of the three possible outcomes:

Case Condition Intuition Result
1 f(n) is polynomially smaller than n^{\log_b a}. The cost is dominated by the vast number of leaf nodes. T(n) = Θ(n^{\log_b a})
2 f(n) is asymptotically equal to n^{\log_b a}. The cost is spread evenly across all levels of the tree. T(n) = Θ(n^{\log_b a} \log n)
3 f(n) is polynomially larger than n^{\log_b a}. The cost is dominated by the work at the root node. T(n) = Θ(f(n))

A useful, though informal, way to think about this comes from the UMD notes on the Master Theorem: think of n^{\log_b a} as the "tree structure" and f(n) as the "node weights". The theorem tells you which of the two has a greater impact on the final sum.


3. Applying the Master Theorem: Examples

The best way to understand the theorem is to apply it. Let's continue with the same MIT video, where the instructor walks through two concrete examples. This demonstrates the systematic process of identifying a, b, and f(n), calculating n^{\log_b a}, and determining which case applies.

Problem Session 2 (MIT 6.006 Introduction to Algorithms, Spring 2020)

Now, let's see the theorem in action. The following segments from the same MIT video show a methodical application of the rules we just learned.

Watch from 11:00 to 16:10 and then from 31:00 to 36:12. In these clips, the instructor solves two recurrences: T(n) = 2T(n/2) + O(√n) T(n) = 8T(n/4) + O(n^(3/2)) Notice his careful, step-by-step approach and his discussion on the nuance between O and Θ in the problem statement—a detail that's important at the principal level.

Let's walk through a few more examples to solidify this process.

Example A: T(n) = 9T(n/3) + n

  1. Identify parameters: a = 9, b = 3, f(n) = n.
  2. Calculate recursion cost: n^{\log_b a} = n^{\log_3 9} = n^2.
  3. Compare: We are comparing f(n) = n with n^2.
  4. Determine case: f(n) = n = O(n^{2 - ε}) for ε=1. This means f(n) is polynomially smaller. This is Case 1.
  5. Conclusion: T(n) = Θ(n^{\log_b a}) = Θ(n^2).

Example B: T(n) = T(2n/3) + 1 (Recurrence for searching in a 2/3-1/3 split)

  1. Identify parameters: a = 1, b = 3/2, f(n) = 1.
  2. Calculate recursion cost: n^{\log_b a} = n^{\log_{3/2} 1} = n^0 = 1.
  3. Compare: We are comparing f(n) = 1 with 1.
  4. Determine case: f(n) = Θ(n^0). The costs are asymptotically equal. This is Case 2.
  5. Conclusion: T(n) = Θ(n^{\log_b a} \log n) = Θ(n^0 \log n) = Θ(\log n).

Example C: T(n) = 3T(n/4) + n \log n

  1. Identify parameters: a = 3, b = 4, f(n) = n \log n.
  2. Calculate recursion cost: n^{\log_b a} = n^{\log_4 3} \approx n^{0.793}.
  3. Compare: We are comparing f(n) = n \log n with n^{0.793}.
  4. Determine case: f(n) is clearly polynomially larger. This is Case 3. (We also need to check a "regularity condition" for Case 3, a f(n/b) ≤ c f(n) for some c < 1. Here, 3 * (n/4) \log(n/4) ≤ (3/4) n \log n, so the condition holds for c=3/4.)
  5. Conclusion: T(n) = Θ(f(n)) = Θ(n \log n).
Test your understanding!

Use the Master Theorem to find the time complexity for the following recurrences:

  1. T(n) = 2T(n/2) + n (Merge Sort)
  2. T(n) = 8T(n/2) + n^2
  3. T(n) = 7T(n/2) + n^3
Show answer
  1. T(n) = 2T(n/2) + n:

    • a=2, b=2, f(n)=n.
    • n^{\log_b a} = n^{\log_2 2} = n^1 = n.
    • f(n) = Θ(n). This is Case 2.
    • T(n) = Θ(n \log n).
  2. T(n) = 8T(n/2) + n^2:

    • a=8, b=2, f(n)=n^2.
    • n^{\log_b a} = n^{\log_2 8} = n^3.
    • f(n) = n^2 = O(n^{3 - ε}) for ε=1. This is Case 1.
    • T(n) = Θ(n^3).
  3. T(n) = 7T(n/2) + n^3:

    • a=7, b=2, f(n)=n^3.
    • n^{\log_b a} = n^{\log_2 7} \approx n^{2.8}.
    • f(n) = n^3 = Ω(n^{2.8 + ε}) for ε=0.2. This is Case 3. (The regularity condition 7(n/2)^3 ≤ c n^3 holds for c=7/8 < 1.)
    • T(n) = Θ(f(n)) = Θ(n^3).

4. First Principles: Why the Master Theorem Works

For someone with your preference for proofs, it's important to understand that the Master Theorem isn't magic. It's a generalization of the recursion tree analysis from our previous lesson. The total work is the sum of the work done at each level of the tree. This sum always consists of two parts: the total work at the leaves and the sum of work at the non-leaf nodes.

The proof of the Master Theorem involves analyzing the geometric series formed by the sum of work at each level and determining whether the sum is dominated by the first term (root cost), the last term (leaf cost), or grows steadily across all terms.

Design and Analysis of Algorithms - UT Arlington

The following lecture slides from UT Arlington provide a formal proof of the Master Theorem. You don't need to work through every line of the derivation, but skimming it will connect the theorem back to the recursion tree and summation principles we've already covered.

Skim through the slides under the headings 'Proof for Exact Powers' and 'Proof of Lemma 4.3'. Focus on the structure of the argument: how it uses a summation g(n) to represent the total work and then evaluates this sum under the three different conditions. This will give you the 'first principles' justification for the theorem's three cases.


5. Knowing the Limits: When the Theorem Fails

A key mark of a senior engineer is knowing the boundaries of a tool. The Master Theorem does not apply to all recurrences. It has "gaps" between its cases.

Consider T(n) = 2T(n/2) + n \log n.

  • a=2, b=2, so n^{\log_b a} = n.
  • Here, f(n) = n \log n is asymptotically larger than n.
  • However, it is not polynomially larger. The ratio f(n) / n^{\log_b a} = (n \log n) / n = \log n. To satisfy Case 3, this ratio would need to be at least n^ε for some ε > 0. Since \log n grows slower than any polynomial n^ε, this recurrence falls into a gap between Case 2 and Case 3.

In such cases, you must fall back on other methods, like the recursion tree or substitution method, to find the solution, which for this example is Θ(n \log^2 n). The Master Theorem is a powerful hammer, but not every problem is a nail.


Conclusion

Today we've added a major time-saving tool to your complexity analysis toolkit. By recognizing a divide-and-conquer recurrence, you can now bypass the manual work of recursion trees and directly classify it into one of three cases to find its complexity.

Key Takeaways:

  • The Master Theorem solves recurrences of the form T(n) = aT(n/b) + f(n).
  • The core of the theorem is a comparison between the work done at each step, f(n), and the cost derived from the recursion pattern, n^{\log_b a}.
  • Case 1 (Leaves Dominate): If f(n) is polynomially smaller, T(n) = Θ(n^{\log_b a}).
  • Case 2 (Balanced Work): If f(n) is asymptotically equal, T(n) = Θ(n^{\log_b a} \log n).
  • Case 3 (Root Dominates): If f(n) is polynomially larger, T(n) = Θ(f(n)).
  • Crucially, the theorem has limitations and does not apply if f(n) falls in the "gaps" between cases.

Next Lesson Preview:
We will conclude our module on fundamental complexity analysis by exploring amortized analysis. This is a different way of thinking about cost, where we average the expense of operations over a sequence. It's the key to understanding why data structures like dynamically-sized arrays (e.g., std::vector in C++ or ArrayList in Java) and hash tables are so efficient in practice, even though individual operations can sometimes be very expensive.