The RAM model, Big Oh, growth rates, dominance relations, and logarithms. The tools that let you compare algorithms without running them.
You have two algorithms that solve the same problem. Both are correct. How do you choose between them? You could time them on your computer, but that tells you about your computer and your test data, not about the algorithms themselves.
What we really want is a way to predict how an algorithm will perform on inputs of any size, on any machine. We want to separate the essential efficiency of the algorithm from the accidents of implementation. That is what algorithm analysis gives us.
The key insight: rather than timing programs, we count the number of fundamental operations as a function of input size n. An algorithm that does 2n2 operations will always eventually lose to one that does 100n operations, no matter how fast the first machine is.
To count operations, we need to agree on what counts as "one operation." The Random Access Machine (RAM) model is our contract. It assumes:
• Each simple operation (+, -, *, /, comparison, assignment) takes one time step.
• Loops and function calls are not simple operations -- they cost the sum of their inner operations.
• Memory access takes one step regardless of address (no caching effects).
The RAM model is a simplification. Real computers have caches, pipelines, and branch predictors. But the simplification works remarkably well because it captures the essential growth rate of running time.
Why worst case? Think of it like a casino trip. The best case (you own the place) is too unlikely to plan for. The average case (you lose 87% of your money) is hard to compute and debatable. The worst case (you lose everything) is easy to calculate and likely enough to matter.
Real complexity functions have bumps and wiggles. An algorithm might run slightly faster when n is a power of 2. We don't care about these details. Big Oh notation lets us ignore constant factors and lower-order terms, focusing on the growth rate.
In plain English: f grows no faster than g, up to a constant factor. The O is an upper bound.
Ω is a lower bound. And when both hold:
Θ is a tight bound. It says f and g grow at the same rate.
See f(n) = 3n2 − 100n + 6 squeezed between c1·n2 and c2·n2. Adjust c to find valid bounds.
The power of Big Oh is that it groups algorithms into classes. Within a class, constant factors don't matter. Between classes, the slower-growing function always wins for large enough n.
Here are the classes that matter, from fastest to slowest growing:
Time at 1 ns/operation for different n values. Red = impractical (over 1 year).
We say a faster-growing function dominates a slower-growing one. The dominance hierarchy is:
Is 2n+1 = Θ(2n)? Yes! Because 2n+1 = 2 · 2n, and constant factors don't matter in Big Oh. But is 22n = Θ(2n)? No! Because 22n = (2n)2, and squaring is NOT a constant factor.
Pick two growth classes to compare. Watch how quickly the dominant function pulls away.
Two rules govern how Big Oh combines:
When you add functions, the dominant one wins. This is why n3 + n2 + n + 1 = O(n3). The smaller terms are small potatoes.
When you multiply, both matter. An O(n) loop inside an O(n) loop gives O(n2). An O(n) loop inside an O(log n) binary search gives O(n log n).
Transitivity: If f(n) = O(g(n)) and g(n) = O(h(n)), then f(n) = O(h(n)). This lets us chain comparisons: since n = O(n log n) and n log n = O(n2), we know n = O(n2).
A logarithm is an inverse exponential. If 2x = n, then x = log2(n). Logarithms arise whenever we repeatedly halve things (binary search), double things (tree height), or multiply things (number of digits).
Bases don't matter in Big Oh. Since loga n = logb n / logb a, changing the base only multiplies by a constant. So O(log2 n) = O(log10 n) = O(ln n). We just write O(log n).
Logarithms grow incredibly slowly. log2(1,000,000) ≈ 20. log2(1,000,000,000) ≈ 30. This is why binary search is so fast. Even on a billion items, it needs only about 30 comparisons.
Drag n across six orders of magnitude. Watch log n barely change.
Time to see growth rates compete head-to-head. This simulation races algorithms with different complexities on the same input size. Watch how the polynomial algorithms pull ahead of exponential ones.
Drag the slider to increase input size n. The bar chart shows the number of operations for each complexity class (log scale). Red means "would take over 1 year at 1 ns/op."
Algorithm analysis is the language we use throughout the rest of the book. Here is how the concepts connect:
| Concept | Where It Leads |
|---|---|
| O(log n) | Chapter 3: BST operations, Chapter 4: binary search |
| O(n log n) | Chapter 4: mergesort, heapsort, quicksort |
| O(n + m) | Chapter 5: BFS and DFS on graphs |
| O(n2) and O(n3) | Chapter 8: dynamic programming tables |
| Exponential/factorial | Chapter 9: NP-completeness |