Algorithms

Foundations: correctness and asymptotic analysis

8 / 454
Contents

Contents

Algorithms

0 of 454 complete

11 min read

Proving a cost cannot be beaten: lower bounds

An upper bound is a promise about every input. A worst-case lower bound needs only one bad input for each size, and finding that family is a skill of its own.

So far every argument has been an upper bound: the procedure does at most this much work. The opposite direction is a different kind of claim and it is argued in a different way. Saying a procedure sometimes does at least this much work is what stops a hopeful reader from believing a faster version exists when it does not.

Start with the practical version of the question. You have timed insertion sort on a few thousand shuffled numbers and it looked acceptable. Is there an input of that size on which it does far more work, and how would you know you had found the worst one rather than merely a bad one?

The way to know is to build the bad input deliberately rather than to search for it. A family of inputs is one input chosen for each size nn, described by a rule rather than listed. For insertion sort, the family is the array that runs strictly downwards: n,n−1,…,2,1n, n-1, \ldots, 2, 1. Run it and compare the count against n2/4n^2/4.

One deliberately bad input for each sizePython

Replace list(range(n, 0, -1)) with list(range(1, n + 1)), which is the same numbers going upwards, and run it again. The counts collapse to n - 1, which is why a lower bound has to name the input it is about.

What the definition asks for

Write Tworst(n)T_{\text{worst}}(n) for the largest number of operations the procedure performs over all inputs of size nn. The claim Tworst(n)=Ω(g(n))T_{\text{worst}}(n) = \Omega(g(n)) unfolds, by the definition from two lessons ago, into: there are fixed c>0c > 0 and n0n_0 with Tworst(n)≥c g(n)T_{\text{worst}}(n) \ge c\,g(n) for every n≥n0n \ge n_0.

Now notice what that lets you get away with. Tworst(n)T_{\text{worst}}(n) is a maximum, so any single input of size nn gives a value that Tworst(n)T_{\text{worst}}(n) is at least as large as. One input per size is therefore enough, and that is the whole reason lower bounds on a named algorithm are provable by hand.

The insertion sort bound, every step written out

Take the family An=(n,n−1,…,2,1)A_n = (n, n-1, \ldots, 2, 1), the array of the first nn whole numbers in decreasing order. Fix a pass number ii, somewhere between 1 and n−1n-1. The value at position ii is n−in - i, and the values at positions 0 to i−1i-1 are n,n−1,…,n−i+1n, n-1, \ldots, n-i+1, every one of them larger than n−in - i.

The inner loop compares the value being placed with the entries to its left and keeps going while they are larger. Since all ii of them are larger, it compares against all ii and only stops by running off the left end. So pass ii costs exactly ii comparisons.

Add the passes up. The passes are i=1,2,…,n−1i = 1, 2, \ldots, n-1, and pass ii costs ii, so the total is 1+2+⋯+(n−1)1 + 2 + \cdots + (n-1), which by the sum from the counting lesson is (n−1)n2\frac{(n-1)n}{2}. For n=64n = 64 that is 63×642=2016\frac{63 \times 64}{2} = 2016, which is what the snippet printed.

Tworst(n)  ≥  ∑i=1n−1i  =  (n−1)n2  ≥  n24for all n≥2T_{\text{worst}}(n) \;\ge\; \sum_{i=1}^{n-1} i \;=\; \frac{(n-1)n}{2} \;\ge\; \frac{n^2}{4} \quad \text{for all } n \ge 2
The first step holds because the worst case is a maximum and this family is one candidate. The last step is checked below.

The last inequality deserves its own line. The claim (n−1)n2≥n24\frac{(n-1)n}{2} \ge \frac{n^2}{4} becomes, after multiplying both sides by 4, 2n(n−1)≥n22n(n-1) \ge n^2, that is 2n2−2n≥n22n^2 - 2n \ge n^2, that is n2−2n≥0n^2 - 2n \ge 0, that is n(n−2)≥0n(n-2) \ge 0. Both factors are non-negative exactly when n≥2n \ge 2, so the inequality holds from n=2n = 2 onwards.

The definition wanted two numbers and now both are in hand: c=14c = \frac{1}{4} and n0=2n_0 = 2. So Tworst(n)=Ω(n2)T_{\text{worst}}(n) = \Omega(n^2) for insertion sort. Put that beside the upper bound Tworst(n)=O(n2)T_{\text{worst}}(n) = O(n^2) proved earlier and the two together give Tworst(n)=Θ(n2)T_{\text{worst}}(n) = \Theta(n^2): the worst case is quadratic, not merely at most quadratic.

A lower bound on the problem, not on one procedure

The bound above is about insertion sort and says nothing about what some cleverer procedure might do. A stronger kind of claim covers every possible algorithm for a task, and the argument for it uses an opponent rather than a family.

Take the maximum problem from the first lesson. The claim is that any correct algorithm must read every one of the nn entries, so no algorithm for it can cost less than Ω(n)\Omega(n). Here is a procedure that tries to save one read, and what happens to it.

Skip one entry and the opponent changes itPython

Change the loop so it skips the entry at position 1 instead, and change which entry the second line rewrites to match. The procedure fails again, which is the point: it is not the last position that matters, it is that a position was skipped.

The general argument is that demonstration with the particular procedure removed. Suppose some algorithm claims to solve the maximum problem and, on the input of nn zeros, finishes without ever reading position jj. Call its answer vv, which must be 0, since 0 is the maximum of that input.

Now build a second input: nn zeros again, except that position jj holds 1. Every cell the algorithm reads holds exactly what it held before, because position jj is the only cell that changed and that is the one cell it does not read. An algorithm whose every read returns the same value takes the same path and produces the same answer, so it answers 0 again. But the maximum of the second input is 1, so the answer is wrong.

The algorithm was assumed correct, so this cannot happen, and the assumption that it skipped a position must be false. It therefore reads all nn positions, which costs at least nn operations. With c=1c = 1 and n0=1n_0 = 1 that is exactly Tworst(n)=Ω(n)T_{\text{worst}}(n) = \Omega(n), and it holds for every algorithm anyone will ever write for this problem.

That kind of opponent, who is allowed to decide what the untouched part of the input holds after seeing what the algorithm did, is called an adversary. The maximum scan from the first lesson costs n−1n - 1 comparisons, which is O(n)O(n), so the problem's lower bound and that algorithm's upper bound meet. When they meet, the problem is settled and no cleverness can help.

ClaimWhat it is aboutHow it is provedWhat it rules out
Upper bound, Oone named algorithman argument covering every input of size nnothing; a better algorithm may exist
Worst-case lower bound, Omega, on an algorithmone named algorithmone family of bad inputs, countedimproving that algorithm's worst case
Lower bound on a problemevery possible algorithman adversary who rewrites what was not readany algorithm at all beating the bound
The two kinds of claim, and what each one is worth.
A colleague times a sorting procedure on a thousand random arrays of size 5000, finds the slowest run took 40 milliseconds, and says its worst case is Omega of that. What is missing?

A worst-case lower bound needs an input constructed to be bad, not the worst of a sample. Random arrays are overwhelmingly likely to miss the expensive family entirely: for insertion sort, a random array of 5000 entries costs about a quarter of n squared, while the reversed array costs about half, and a family designed against a quicksort implementation can be thousands of times worse than anything random. Measurement can only ever show that some input is at least this bad. To claim the worst case, you have to name the family and count it on paper.

Check yourself

You want to prove that a particular searching procedure has worst case Omega(n). What is the smallest thing you must supply?

All lessons in Algorithms