Algorithms

Foundations: correctness and asymptotic analysis

7 / 454
Contents

Contents

Algorithms

0 of 454 complete

12 min read

Working with the notation

The equals sign is not equals, a Theta inside a formula means something specific, and two functions need not be comparable at all.

The definitions from the previous lesson are rarely used directly. In practice you combine known results with a handful of rules, and the rules are worth deriving once so that they are not magic. Start with what happens when two costs are added together, which is what happens whenever one phase of an algorithm is followed by another.

Suppose a procedure scans a list once, costing about nn, and then does something costing about n2n^2. The total is n+n2n + n^2. Compare that with the larger of the two on its own.

A sum against its own largest termPython

Change small to n * n so both terms match, and run it again. What does the ratio settle on now?

The ratio starts at 2 and falls towards 1. The smaller term never disappears, but its share of the total does. That is the sum rule: a sum of two functions grows exactly like whichever of them is larger.

What the equals sign means here

Writing f(n)=O(g(n))f(n) = O(g(n)) is an abuse of notation that everybody uses. O(g(n))O(g(n)) is really a set of functions, namely all the functions bounded above by some constant multiple of gg eventually, and the honest statement is f∈O(g)f \in O(g). The equals sign is read left to right and means membership.

So n=O(n2)n = O(n^2) is a correct sentence and O(n2)=nO(n^2) = n is not, in the same way that saying a robin is a bird is correct and saying a bird is a robin is not. Two claims of the form f=O(h)f = O(h) and g=O(h)g = O(h) do not let you conclude f=gf = g, however tempting the symbols make it look. If f(n)=nf(n) = n and g(n)=n2g(n) = n^2 then both are O(n2)O(n^2) and they are not remotely equal.

Notation standing inside a formula

You will see expressions like 2n2+Θ(n)2n^2 + \Theta(n). This means: there is some function hh with h=Θ(n)h = \Theta(n) such that the quantity equals 2n2+h(n)2n^2 + h(n). The notation is standing in for a term whose exact form does not matter, only its growth.

When such an expression appears on the right of an equals sign, the meaning is that for every choice of the anonymous function on the left there is some choice on the right making the equation true. That is what makes a chain like T(n)=2n2+Θ(n)=Θ(n2)T(n) = 2n^2 + \Theta(n) = \Theta(n^2) legitimate to read in one direction only, from left to right.

The rules worth memorising

RuleStatement
Constants vanishFor any constant a > 0, a f(n) = Theta(f(n))
Sum ruleO(f) + O(g) = O(max of f and g), and likewise for Theta
Product ruleIf f1 = O(g1) and f2 = O(g2) then f1 f2 = O(g1 g2)
TransitivityIf f = O(g) and g = O(h) then f = O(h). The same holds for Omega, Theta, o and omega
Reflexivityf = O(f), f = Omega(f), f = Theta(f). But f is never o(f) or omega(f)
Symmetry of Thetaf = Theta(g) exactly when g = Theta(f)
Transpose symmetryf = O(g) exactly when g = Omega(f), and f = o(g) exactly when g = omega(f)
Theta is bothf = Theta(g) exactly when f = O(g) and f = Omega(g) both hold
Each of these follows from the definitions by producing the constants, which the four derivations below do for the ones used most.

Here is the sum rule proved, since it is the one used most. Suppose f=O(h)f = O(h) with constants c1,n1c_1, n_1, and g=O(h)g = O(h) with constants c2,n2c_2, n_2. For every n≥max⁡(n1,n2)n \ge \max(n_1, n_2) both inequalities apply at once, so f(n)+g(n)≤c1h(n)+c2h(n)=(c1+c2)h(n)f(n) + g(n) \le c_1 h(n) + c_2 h(n) = (c_1 + c_2)h(n). Taking c=c1+c2c = c_1 + c_2 and n0=max⁡(n1,n2)n_0 = \max(n_1, n_2) shows f+g=O(h)f + g = O(h). Two constants went in and one came out, which is why adding a constant number of phases never changes the growth.

And here is why constants vanish. If f(n)=7nf(n) = 7n then choosing c1=7c_1 = 7 and c2=7c_2 = 7 with n0=1n_0 = 1 gives 7n≤7n≤7n7n \le 7n \le 7n, so 7n=Θ(n)7n = \Theta(n). The constant is absorbed into the cc that the definition allowed you to pick, which is exactly the point of allowing it.

Transitivity, and why the constants multiply

Transitivity is the rule that lets you take two bounds you already know and join them into a third. It says that if f=O(g)f = O(g) and g=O(h)g = O(h) then f=O(h)f = O(h). Like every rule here it is proved by producing the pair of constants.

Unfold what the two assumptions give you. From f=O(g)f = O(g) there is a c1>0c_1 > 0 and an n1n_1 with f(n)≤c1g(n)f(n) \le c_1 g(n) for every n≥n1n \ge n_1. From g=O(h)g = O(h) there is a c2>0c_2 > 0 and an n2n_2 with g(n)≤c2h(n)g(n) \le c_2 h(n) for every n≥n2n \ge n_2. Take n0=max⁡(n1,n2)n_0 = \max(n_1, n_2), so that beyond n0n_0 both statements are available at the same time.

Now chain them. Multiplying g(n)≤c2h(n)g(n) \le c_2 h(n) through by c1c_1, which is positive and so leaves the direction alone, gives c1g(n)≤c1c2h(n)c_1 g(n) \le c_1 c_2 h(n). Put that after the first inequality and you get f(n)≤c1g(n)≤c1c2h(n)f(n) \le c_1 g(n) \le c_1 c_2 h(n) for every n≥n0n \ge n_0. The pair c=c1c2c = c_1 c_2 and n0=max⁡(n1,n2)n_0 = \max(n_1, n_2) satisfies the definition, so f=O(h)f = O(h).

f≤c1g beyond n1,g≤c2h beyond n2   ⟹   f≤c1c2 h beyond max⁡(n1,n2)f \le c_1 g \ \text{beyond } n_1, \quad g \le c_2 h \ \text{beyond } n_2 \ \implies\ f \le c_1 c_2\, h \ \text{beyond } \max(n_1, n_2)
Transitivity for O. The constants multiply and the starting points are pushed to the later of the two.

With real numbers in it: let f(n)=3n+4f(n) = 3n + 4, g(n)=ng(n) = n and h(n)=n2h(n) = n^2. For n≥4n \ge 4, 3n+4≤3n+n=4n3n + 4 \le 3n + n = 4n, so c1=4c_1 = 4 and n1=4n_1 = 4. For n≥1n \ge 1, n≤n2n \le n^2, so c2=1c_2 = 1 and n2=1n_2 = 1. The rule hands back c=4×1=4c = 4 \times 1 = 4 and n0=4n_0 = 4, and at n=4n = 4 that reads 16≤6416 \le 64, which holds and keeps holding.

The product rule, and the nested loop it licenses

The product rule is the one every nested-loop analysis leans on. It says that if f1=O(g1)f_1 = O(g_1) and f2=O(g2)f_2 = O(g_2), then the product f1f2=O(g1g2)f_1 f_2 = O(g_1 g_2). The proof is built the same way, and it uses the fact that the definition of OO requires the functions to be non-negative.

Suppose f1(n)≤c1g1(n)f_1(n) \le c_1 g_1(n) for every n≥n1n \ge n_1 and f2(n)≤c2g2(n)f_2(n) \le c_2 g_2(n) for every n≥n2n \ge n_2, and again set n0=max⁡(n1,n2)n_0 = \max(n_1, n_2) so both hold together. Multiply the first inequality through by f2(n)f_2(n), which is at least 0, to get f1(n)f2(n)≤c1g1(n)f2(n)f_1(n) f_2(n) \le c_1 g_1(n) f_2(n). Multiplying by a non-negative quantity cannot flip the direction.

Then multiply the second inequality through by c1g1(n)c_1 g_1(n), which is also at least 0, to get c1g1(n)f2(n)≤c1c2g1(n)g2(n)c_1 g_1(n) f_2(n) \le c_1 c_2 g_1(n) g_2(n). Joining the two lines gives f1(n)f2(n)≤c1c2g1(n)g2(n)f_1(n) f_2(n) \le c_1 c_2 g_1(n) g_2(n) for every n≥n0n \ge n_0, so c=c1c2c = c_1 c_2 and that n0n_0 prove the rule.

Here is the case it exists for. Procedure B in the first lesson had a loop over every reading, and inside it a second loop over every reading. Call f1(n)f_1(n) the number of times the outer loop runs, which is nn, and f2(n)f_2(n) the cost of one full pass of the outer body, which is one complete inner loop and is at most some constant times nn. Then f1=O(n)f_1 = O(n) and f2=O(n)f_2 = O(n), and the product rule gives a total of O(n⋅n)=O(n2)O(n \cdot n) = O(n^2). On the six readings that was 36 comparisons, which is 626^2.

Comparing two functions in practice

A question like is $n \log_2 n$ bounded above by $n^{1.5}$ is answered by finding the crossing point and then arguing it never uncrosses. The comparison reduces to log⁡2n\log_2 n against n\sqrt{n}, since both sides carry a factor of nn.

log against square rootPython

Add n = 10 and n = 12 to the list and run it again. Find every n where the answer is no.

At n=16n = 16 both sides equal 4. Before that the logarithm is briefly ahead, and after it the square root pulls away and never gives the lead back. So log⁡2n≤n\log_2 n \le \sqrt{n} for all n≥16n \ge 16, and multiplying both sides by nn gives nlog⁡2n≤n1.5n\log_2 n \le n^{1.5} for all n≥16n \ge 16. With c=1c = 1 and n0=16n_0 = 16 the definition is satisfied and nlog⁡2n=O(n1.5)n\log_2 n = O(n^{1.5}).

Not everything is comparable

For ordinary numbers, exactly one of a<ba < b, a=ba = b, a>ba > b holds. Growth rates have no such guarantee. Consider f(n)=nf(n) = n and g(n)=n1+sin⁡ng(n) = n^{1 + \sin n}. The exponent of gg swings between 0 and 2 forever, so gg is sometimes far below ff and sometimes far above it, and no constant multiple ever settles the matter in either direction.

Neither f=O(g)f = O(g) nor g=O(f)g = O(f) is true. This almost never happens to a real running time, but it is why the notation is defined with quantifiers rather than by saying one function is bigger than another.

An algorithm reads its input once (cost about n), then sorts a fixed 100 items (cost some constant), then scans again (cost about n). What is the total, as a Theta?

Theta(n). Adding three phases means adding three functions: n, a constant, and n. By the sum rule the total grows like the largest of them, which is n. The constant middle phase does not depend on n at all, so however slow that sort is in seconds, it is absorbed into the constant factor. Two scans instead of one is also absorbed, since 2n = Theta(n).

Check yourself

Someone writes: since insertion sort is O(n squared), and merge sort is O(n log n), insertion sort is definitely slower on every input. What is wrong?

All lessons in Algorithms