Growth Rates and Big O
Why we count steps instead of seconds, and how to prove an asymptotic bound from the definition.
Lessons in this course
Foundations
01Growth Rates and Big O- 02Recurrences and the Master Theorem
Going further
03Amortized Analysis
Why we count steps
Timing a program tells you about one machine, one compiler and one input. Change any of them and the number changes. To compare algorithms rather than computers, we count basic operations (a comparison, an addition, an array access) as a function of the input size , and ask how that count grows.
def linear_search(items, target):
for i, item in enumerate(items):
if item == target:
return i
return -1If target is absent, the loop makes comparisons. If it sits at the front, one. When nothing else is said, we analyse the worst case, because it is a guarantee: no input of size can cost more.
Exact counts are fragile, and whether an iteration is three operations or five does not change the shape of the curve. Asymptotic notation keeps the shape and discards the rest.
The three definitions
Let and be functions from the positive integers to the non-negative reals.
Upper bound. if there are constants and such that
Lower bound. if there are constants and such that
Tight bound. if both hold, that is, if there are constants and such that
Two constants do all the work: says we do not care about constant factors, and says we do not care about small inputs. The equals sign is a historical abuse of notation. is really a set of functions and is a member of it, so read it as "is", never as an equation you can turn around.
A worked proof
Claim. .
To prove it we must produce a and an . For every we have , so
Choose and . That is the entire proof.
The witnesses are not unique. For we have , so , works just as well. A smaller usually costs a larger .
The lower bound is easier. for all , so with . Both bounds together give .
Common growth rates
| Growth | Name | Typical source | Steps at |
|---|---|---|---|
| constant | array index, hash lookup | ||
| logarithmic | binary search | ||
| linear | one pass over the input | ||
| linearithmic | merge sort | ||
| quadratic | all pairs, insertion sort | ||
| cubic | naive matrix multiplication | ||
| exponential | all subsets | ||
| factorial | all permutations |
A machine doing a billion steps per second finishes the first six rows in a second or less. It will not finish the last two before the sun burns out. That gap, not a factor of two here or there, is what asymptotic analysis is for.
Common mistakes
- Reading as "exactly". is true, and useless. is an upper bound. When you mean a tight bound, say .
- Confusing the bound with the case. Worst, best and average case choose which function to study. , and describe how that function grows. Insertion sort is in the worst case and in the best, and both statements are tight.
- Keeping constants. is simply . The base of a logarithm disappears the same way, since and differ only by the constant factor .
- Dropping constants from an exponent. That rule does not extend upstairs. is not .
- Forgetting . An algorithm can lose to an one on small inputs. This is why real sorting libraries switch to insertion sort for short runs.
Exercises
1. Prove from the definition that . Give explicit values of and .
Show the solution
For we have , so . Take and . Other pairs work too, such as with .
2. True or false: (a) , (b) .
Show the solution
(a) True. , so and satisfy the definition.
(b) False. . If held for all large , dividing by would give , which no constant satisfies. Adding a constant to an exponent multiplies by a constant. Multiplying the exponent changes the growth rate.
3. How many times does the comparison on the highlighted line run, as a function of ? Give a bound and justify it.
def count_pairs(items):
n = len(items)
count = 0
for i in range(n):
for j in range(i + 1, n):
if items[i] == items[j]:
count += 1
return countShow the solution
For a fixed the inner loop runs times, so the comparison runs
times, on every input. For the upper bound, . For the lower bound, when we have , so . With , and , the count is .