MIT 6.1220 — Lecture 2

Divide and Conquer

Divide and Conquer. Given any problem of size NN, we…

The runtime T(N)T(N) is recursive: T(N)=AT(NB)+(time to divide)+(time to combine)T(N) = A \cdot T\left(\frac{N}{B}\right) + \text{(time to divide)} + \text{(time to combine)}.

Problem. (Rank Finding) Let AA be an unsorted array of NN numbers, and let r[1,N]r \in [1, N] be a rank.

Output the rthr^{\text{th}} smallest number (i.e. the unique number with rank rr).

Example. If r=1r = 1 or r=Nr = N, this asks for the minimum / maximum. If r=N+12r = \frac{N + 1}{2}, this asks for the median.

Solution. (Rank Finding #1) For each xAx \in A, compute L(x):={yAy<x}L(x) := \{ y \in A \mid y < x\}. Find the xx with L(x)=r1|L(x)| = r - 1.

Unfortunately, finding L(x)L(x) takes O(N)O(N) time, and so this is an O(N2)O(N^2) algorithm. But this is also extremely wasteful:

Observation. If L(x)r|L(x)| \geq r, then our target must be in L(x)L(x).

Solution. (Rank Finding #2) Pick a pivot xAx \in A, and consider L(x):={yAy<x}L(x) := \{ y \in A \mid y < x\} and G(x):={yAy>x}G(x) := \{ y \in A \mid y > x \}. Then recurse either on L(x)L(x) or G(x)G(x), depending on whether L(x)r|L(x)| \geq r.

Unfortunately, picking the pivot is important. If we unluckily always pick x=min(A)x = \min(A), this is O(N2)O(N^2) worst-case.

Observation. If an oracle could tell us the median in O(N)O(N) time,
then we'd have T(N)=T(N2)+O(N)T(N) = T\left(\frac{N}{2}\right) + O(N), which implies T(N)=O(N)T(N) = O(N).

In fact, we don't even need to pick exactly the median.

Definition. For any 12c<1\frac{1}{2} \leq c < 1, we say xAx \in A is cc-balanced if max{L(x),G(x)}cN\max \{ |L(x)|, |G(x)|\} \leq cN.

Observation. If an oracle could tell us a cc-balanced element in O(N)O(N) time,
then we'd have T(N)=T(cN)+O(N)T(N) = T\left(cN\right) + O(N), which implies T(N)=O(N)T(N) = O(N).

How do we find a cc-balanced element? The trick is to use Rank Finding again!

Solution. (Rank Finding #3) Divide AA into N5\frac{N}{5} buckets of size 55. Say MM is the set of all bucket medians, with M=N5|M| = \frac{N}{5}. Then set x=RankFindFast(M,M2)x = \textsc{RankFindFast}\left(M, \frac{|M|}{2}\right) as the pivot, and proceed from there.

Proof: For algorithm correctness, it suffices to show:

Claim. Our choice of pivot xx is 710\frac{7}{10}-balanced.

Proof: The constant 710\frac{7}{10} is 112×351 - \frac{1}{2} \times \frac{3}{5}. More generally, for buckets of size 2k+12k + 1, we have c=3k+14k+2c = \frac{3k + 1}{4k + 2}.   \square

Now let's discuss the time complexity. The recursion for this problem looks like:

T(N)=O(N)+T(N5)+O(N)+T(7N10)=T(N5)+T(7N10)+c1N,T(N) = O(N) + T\left(\frac{N}{5}\right) + O(N) + T\left(\frac{7N}{10}\right) = T\left(\frac{N}{5}\right) + T\left(\frac{7N}{10}\right) + c_1 \cdot N,

corresponding to (i) constructing MM, (ii) finding xx, (iii) computing L(x)L(x) and G(x)G(x), and (iv) solving the subproblem.

Now we show by induction that T(N)c2NT(N) \leq c_2N for some c2>0c_2 > 0. The inductive step is:

T(N)=T(N5)+T(7N10)+c1Nc2(9N10)+c1N.T(N) = T\left(\frac{N}{5}\right) + T\left(\frac{7N}{10}\right) + c_1 \cdot N \leq c_2 \cdot \left(\frac{9N}{10}\right) + c_1 \cdot N.

And so of course c2=10c1c_2 = 10c_1 works, for example.   \blacksquare

Remark. The key success of this algorithm is that 12k+1+3k+14k+2<1\frac{1}{2k + 1} + \frac{3k + 1}{4k + 2} < 1 when k2k \geq 2, meaning our algorithm does meaningfully cut down our search space by a factor.

The reason why k=2k = 2 is better than k=50k = 50 is in step (i) constructing MM. Even though 12k+1+3k+14k+2\frac{1}{2k + 1} + \frac{3k + 1}{4k + 2} tends to 34\frac{3}{4} for very large kk, the constant c1c_1 also grows very large for large kk.

Problem. (Integer Multiplication) Given two NN-bit numbers aa and bb, compute their product aba \cdot b.

Solution. (Karatsuba) Write a=y+2N/2xa = y + 2^{N/2} \cdot x and b=z+2N/2wb = z + 2^{N/2} \cdot w, so a=[x][y]a = [x] [y] and b=[w][z]b = [w] [z] and:

ab= 2Nxw+2N/2(xz+yw)+yz= (2N2N/2)xw+2N/2(x+y)(w+z)+(12N/2)yz.\begin{align*}ab = \ & 2^N \cdot xw + 2^{N / 2} \cdot (xz + yw) + yz \\ = \ & (2^N - 2^{N / 2}) \cdot xw + 2^{N / 2} \cdot (x + y)(w + z) + (1 - 2^{N / 2}) \cdot yz.\end{align*}

So we only need xwxw, yzyz, and (x+y)(w+z)(x + y)(w + z), which yields T(N)=3T(N2)+O(N)T(N) = 3T\left(\frac{N}{2}\right) + O(N), or T(N)=O(Nlog2(3))T(N) = O(N^{\log_2(3)}).

In 1971, the Fast Fourier Transform was used to find an O(Nlog(N)log(log(N)))O(N \cdot \log(N) \cdot \log(\log(N))) solution.

In 2007, O(Nlog(N)2log(N))O(N \cdot \log(N) \cdot 2^{\log^*(N)}) was found, where log(N)\log^*(N) is the # of log\log's with log(log((log(N))))<1\log(\log(\dots(\log(N))\dots)) < 1.

And then in 2019, an O(Nlog(N))O(N \cdot \log(N)) solution was found. And we still don't know if we can do better.