Divide and Conquer. Given any problem of size N, we…
Divide. Split the problem into A subproblems, each of size BN.
Conquer. Solve each subproblem recursively.
Combine. Use the subproblem solutions to get a solution of the original problem.
The runtime T(N) is recursive: T(N)=A⋅T(BN)+(time to divide)+(time to combine).
Problem. (Rank Finding) Let A be an unsorted array of N numbers, and let r∈[1,N] be a rank.
Output the rth smallest number (i.e. the unique number with rank r).
Example. If r=1 or r=N, this asks for the minimum / maximum. If r=2N+1, this asks for the median.
Solution. (Rank Finding #1) For each x∈A, compute L(x):={y∈A∣y<x}. Find the x with ∣L(x)∣=r−1.
Unfortunately, finding L(x) takes O(N) time, and so this is an O(N2) algorithm. But this is also extremely wasteful:
Observation. If ∣L(x)∣≥r, then our target must be in L(x).
Solution. (Rank Finding #2) Pick a pivot x∈A, and consider L(x):={y∈A∣y<x} and G(x):={y∈A∣y>x}. Then recurse either on L(x) or G(x), depending on whether ∣L(x)∣≥r.
Unfortunately, picking the pivot is important. If we unluckily always pick x=min(A), this is O(N2) worst-case.
Observation. If an oracle could tell us the median in O(N) time, then we'd have T(N)=T(2N)+O(N), which implies T(N)=O(N).
In fact, we don't even need to pick exactly the median.
Definition. For any 21≤c<1, we say x∈A is c-balanced if max{∣L(x)∣,∣G(x)∣}≤cN.
Observation. If an oracle could tell us a c-balanced element in O(N) time, then we'd have T(N)=T(cN)+O(N), which implies T(N)=O(N).
How do we find a c-balanced element? The trick is to use Rank Finding again!
Solution. (Rank Finding #3) Divide A into 5N buckets of size 5. Say M is the set of all bucket medians, with ∣M∣=5N. Then set x=RankFindFast(M,2∣M∣) as the pivot, and proceed from there.
Proof: For algorithm correctness, it suffices to show:
Claim. Our choice of pivot x is 107-balanced.
Proof: The constant 107 is 1−21×53. More generally, for buckets of size 2k+1, we have c=4k+23k+1. □
Now let's discuss the time complexity. The recursion for this problem looks like:
corresponding to (i) constructing M, (ii) finding x, (iii) computing L(x) and G(x), and (iv) solving the subproblem.
Now we show by induction that T(N)≤c2N for some c2>0. The inductive step is:
T(N)=T(5N)+T(107N)+c1⋅N≤c2⋅(109N)+c1⋅N.
And so of course c2=10c1 works, for example. ■
Remark. The key success of this algorithm is that 2k+11+4k+23k+1<1 when k≥2, meaning our algorithm does meaningfully cut down our search space by a factor.
The reason why k=2 is better than k=50 is in step (i) constructing M. Even though 2k+11+4k+23k+1 tends to 43 for very large k, the constant c1 also grows very large for large k.
Problem. (Integer Multiplication) Given two N-bit numbers a and b, compute their product a⋅b.
Solution. (Karatsuba) Write a=y+2N/2⋅x and b=z+2N/2⋅w, so a=[x][y] and b=[w][z] and: