Algorithms

Foundations: correctness and asymptotic analysis

9 / 454
Contents

Contents

Algorithms

0 of 454 complete

10 min read

The ladder of growth rates

Constant, logarithmic, linear, n log n, polynomial, exponential and factorial, plus the two odd ones that turn up in real analysis.

Almost every running time you meet falls into a short list of shapes, and knowing that list by heart is what lets you glance at an algorithm and know whether it is worth writing. The differences between the shapes are much larger than most people expect, so it is worth watching them once at full scale.

The following table prints, for a few input sizes, what each shape evaluates to. Read the last two columns carefully.

The same n, five different costsPython

Add 100 to the list and run it again. Count the digits in the last two entries of that row.

At n=60n = 60 the quadratic column is 3600, which any machine finishes instantly. The 2n2^n column is about 1.15×10181.15 \times 10^{18}, which at a billion operations a second takes about 36 years. The factorial column is about 8×10818 \times 10^{81}, which is a number of the same order as the count of atoms in the observable universe.

The classes, in order

GrowthNameWhere it comes fromLargest n that is comfortable
Theta(1)constanta fixed amount of work, whatever the inputany
Theta(log n)logarithmichalving the problem a few timesany
Theta(sqrt n)roottrying divisors up to the square root of a numberabout 10^16
Theta(n)linearone pass over the inputabout 10^8
Theta(n log n)linearithmica pass for each of log n roundsabout 10^6 to 10^7
Theta(n^2)quadraticevery pair of itemsabout 10^4
Theta(n^3)cubicevery triple, or multiplying matrices the obvious wayabout 500
Theta(2^n)exponentialevery subset of n itemsabout 25
Theta(n!)factorialevery ordering of n itemsabout 11
Each row grows strictly faster than the row above it, in the little-o sense.

A polynomial cost is one bounded by nkn^k for some fixed kk: linear, quadratic and cubic are all polynomial. An exponential cost has nn in the exponent, like 2n2^n or 3n3^n. Every polynomial is eventually beaten by every exponential, however extreme the exponents look: n100n^{100} is smaller than 1.01n1.01^n for large enough nn, though that nn is astronomically large.

Why the base of a logarithm is not written

A logarithm answers the question: to what power must the base be raised to reach this number? Since 210=10242^{10} = 1024, log⁡21024=10\log_2 1024 = 10. Changing base only multiplies by a fixed number, because log⁡bn=log⁡2n/log⁡2b\log_b n = \log_2 n / \log_2 b, and log⁡2b\log_2 b does not depend on nn.

So log⁡10n\log_{10} n and log⁡2n\log_2 n differ by the constant factor log⁡210≈3.32\log_2 10 \approx 3.32, and constants vanish inside Theta. That is why Θ(log⁡n)\Theta(\log n) is written with no base at all. The one place the base does matter is inside an exponent: 2n2^n and 3n3^n are genuinely different, since their ratio (3/2)n(3/2)^n grows without bound.

Two bases, one constant apartPython

Add math.log(n), which is the natural logarithm, as a fourth column. Its ratio to log2 is another constant. Which one?

Harmonic sums

One pattern turns up so often that its answer is worth knowing outright. A loop whose inner work shrinks as n/1n/1, then n/2n/2, then n/3n/3, and so on, adds up to nn times the harmonic number Hn=1+1/2+1/3+⋯+1/nH_n = 1 + 1/2 + 1/3 + \cdots + 1/n.

Adding a half, a third, a quarter, foreverPython

The difference settles on a number near 0.577. Push the loop to ten million and watch how little it moves.

The sum of a million reciprocals is only about 14.39, and it stays about ln⁡n\ln n plus a fixed number close to 0.5772. So Hn=Θ(log⁡n)H_n = \Theta(\log n), and a loop costing n/1+n/2+⋯+n/n=nHnn/1 + n/2 + \cdots + n/n = n H_n costs Θ(nlog⁡n)\Theta(n \log n), not Θ(n2)\Theta(n^2) as the first term alone might suggest.

The iterated logarithm

The iterated logarithm, written log⁡∗n\log^* n, counts how many times you must take the logarithm before the result drops to 1 or below. It grows so slowly that for every input any real computer will ever hold, its value is at most 5.

A function that is 5 for every practical inputPython

Add 65536 and 65537 to the list and run it again. The answer climbs from 4 to 5 between those two, and it does not reach 6 until n passes 2 to the power 65536, a number with about twenty thousand digits.

The first four classes, on small inputsEven at n = 20 the quadratic curve has left the others behind. The y axis is clamped, so the quadratic line runs off the top.
065130195260noperationsn squaredn log2 nnlog2 n

Put a million items through each of those two shapes and count. A linear pass is 10610^6 operations, which at a hundred million operations a second is about a hundredth of a second. A quadratic one is (106)2=1012(10^6)^2 = 10^{12} operations, which at the same rate is 10410^4 seconds, or a little under three hours.

A loop runs n times, and on pass number i it does i units of work. What is the total, and how does it compare with a loop that does n units of work on every pass?

The total is 1 + 2 + ... + n = n(n+1)/2, which is Theta(n squared). The comparison loop does n times n = n squared units, also Theta(n squared). They differ by a factor of about 2, which asymptotic notation deliberately discards. Contrast this with the harmonic case, where pass i does n/i units: that total is Theta(n log n), a genuinely different class, because the work shrinks fast enough to change the shape rather than only the constant.

Check yourself

Which pair of functions grow at the same rate, in the Theta sense?

All lessons in Algorithms