Reductions, Recursion and Divide and Conquer
Reductions, Recursion and Divide and Conquer
Naive algorithm:
for i = 1 to n − 1 do
for j = i + 1 to n do
if (A[i] = A[j])
return YES
return NO
Sort A
for i = 1 to n − 1 do
if (A[i] = A[i + 1]) then
return YES
return NO
1
Running time: O(n) plus time to sort an array of n numbers
Important point: algorithm uses sorting as a black box
5.2 Recursion
5.2.0.5 Recursion
Reduction: reduce one problem to another
5.2.0.6 Recursion
(A) Recursion is a very powerful and fundamental technique
(B) Basis for several other methods
(A) Divide and conquer
(B) Dynamic programming
(C) Enumeration and branch and bound etc
(D) Some classes of greedy algorithms
(C) Makes proof of correctness easy (via induction)
(D) Recurrences arise in analysis
2
shall have been thus transferred from the needle on which at the creation God placed them to
one of the other needles, tower, temple, and Brahmins alike will crumble into dust, and with a
thunderclap the world will vanish.
Of course, being good computer scientists, we read this story and immediately substitute n for the
hardwired constant sixty-four.4 How can we move a tower of n disks from one needle to another,
using a third needles as an occasional placeholder, never placing any disk on top of a smaller disk?
recursion
2
The Tower of Hanoi algorithm; ignore everything but the bottom disk
Our algorithm does make one subtle but important assumption: there is a largest disk. In other
words, our recursive algorithm works for any n ≥ 1, but it breaks down when n = 0. We must
handle that base case directly. Fortunately, the monks at Benares, being good Buddhists, are quite
adept at moving zero disks from one needle to another.
SelectSort(A[1..n]): The base case for the Tower of Hanoi algorithm; there is no bottom disk
if n = 1
While it’s tempting return
to think about how all those smaller disks get moved—in other words,
what happens
Find when the recursion
smallest numberis unfolded—it’s
in A. notLet necessary.
A[i] In befact, for more complicated
smallest number
problems, opening up the recursion is a distraction. Our only task is to reduce the problem to one
Swap A[1] and A[i]
or more simpler instances, or to solve the problem directly if such a reduction is impossible. Our
SelectSort(A[2..n])
algorithm is trivially correct when n = 0. For any n ≥ 1, the Recursion Fairy correctly moves (or
more formally, the inductive hypothesis implies that our algorithm correctly moves) the top n − 1
disks, so our algorithm is clearly correct.
Here’s the recursive Hanoi algorithm in more typical pseudocode.
Let T (n) denote the number of moves required to transfer n disks—the running time of our
algorithm. Our vacuous base case implies that T (0) = 0, and the more general recursive algorithm
2
T (n) = Θ(n ). implies that T (n) = 2T (n − 1) + 1 for any n ≥ 1. The annihilator method lets us quickly derive a
closed form solution T (n) = 2n − 1 . In particular, moving a tower of 64 disks requires 264 − 1 =
18,446,744,073,709,551,615 individual moves. Thus, even at the impressive rate of one move per
second, the monks at Benares will be at work for approximately 585 billion years before, with a
thunderclap, the world will vanish.
5.2.0.8 Tower The
of Hanoi
Hanoialgorithm has two very simple non-recursive formulations, for those of us who do
not have an army of assistants to take care of smaller piles. Let’s label the needles 0, 1, and 2,
3
5.2.0.9 Tower of Hanoi via Recursion
5.2.0.11 Analysis
T (n) = 2T (n − 1) + 1
= 22 T (n − 2) + 2 + 1
= ...
= 2i T (n − i) + 2i−1 + 2i−2 + . . . + 1
= ...
= 2n−1 T (1) + 2n−2 + . . . + 1
= 2n−1 + 2n−2 + . . . + 1
= (2n − 1)/(2 − 1) = 2n − 1
Pegs numbered 0, 1, 2
Non-recursive Algorithm 1:
(A) Always move smallest disk forward if n is even, backward if n is odd.
(B) Never move the same disk twice in a row.
(C) Done when no legal move.
Non-recursive Algorithm 2:
(A) Let ρ(n) be the smallest integer k such that n/2k is not an integer. Example: ρ(40) = 4,
ρ(18) = 2.
(B) In step i move disk ρ(i) forward if n − i is even and backward if n − i is odd.
Moves are exactly same as those of recursive algorithm. Prove by induction.
4
5.3 Divide and Conquer
5.3.0.13 Divide and Conquer Paradigm
Divide and Conquer is a common and useful type of recursion
Approach
(A) Break problem instance into smaller instances - divide step
(B) Recursively solve problem on smaller instances
(C) Combine solutions to smaller instances to obtain a solution to the original instance -
conquer step
Question: Why is this not plain recursion?
(A) In divide and conquer, each smaller instance is typically at least a constant factor smaller
than the original instance which leads to efficient running times.
(B) There are many examples of this particular type of recursion that it deserves its own
treatment.
ALGOR I T H M S
AGLOR H I M ST
5
5.4.2.2 Merging Sorted Arrays
(A) Use a new array C to store the merged array
(B) Scan A and B from left-to-right, storing elements in C in order
AGLOR H I M ST
AGH I LM ORST
(C) Merge two arrays using only constantly more extra space (in-place merge sort): doable
but complicated and typically impractical
5.4.3 Analysis
5.4.3.1 Running Time
T (n): time for merge sort to sort an n element array
3. Sum over all levels. The number of levels is log n. So total is cn log n = O(n log n)
6
n cn
7
5.4.5.4 Induction Step
We have
T (n) = T (bn/2c) + T (dn/2e) + cn
≤ 2cbn/2c logbn/2c + 2cdn/2e logdn/2e + cn (by induction)
≤ 2cbn/2c logdn/2e + 2cdn/2e logdn/2e + cn
≤ 2c(bn/2c + dn/2e) logdn/2e + cn
≤ 2cn logdn/2e + cn
≤ 2cn log(2n/3) + cn (since dn/2e ≤ 2n/3 for all n ≥ 2)
≤ 2cn log n + cn(1 − 2 log 3/2)
≤ 2cn log n + cn(log 2 − log 9/4)
≤ 2cn log n
Typically we don’t know upfront what constant to choose. Instead we assume that
T (n) ≤ αcn log n for some constant α that will be fixed later. All we need to prove that
there is some sufficiently large constant α that will make the algebra go through.
Typically you do the algebra with α and then show at the end that α can be chosen to
be sufficiently large constant.
8
5.4.5.7 Selection Sort vs Merge Sort
(A) Selection Sort spends O(n) work to reduce problem from n to n − 1 leading to O(n2 )
running time.
(B) Merge Sort spends O(n) time after reducing problem to two instances of size n/2 each.
Running time is O(n log n)
Question: Merge Sort splits into 2 (roughly) equal sized arrays. Can we do better by
splitting into more than 2 arrays? Say k arrays of size n/k each?
Quick Sort[Hoare]
2. Split array into 3 subarrays: those smaller than pivot, those larger than pivot, and the
pivot itself. Linear scan of array does it. Time is O(n)
Example:
(A) array: 16, 12, 14, 20, 5, 3, 18, 19, 1
(B) pivot: 16
(C) split into 12, 14, 5, 3, 1 and 20, 19, 18 and recursively sort
(D) put them together with pivot in middle
In the worst case T (n) = T (n − 1) + O(n), which means T (n) = O(n2 ). Happens if
array is already sorted and pivot is always first element.
9
5.6 Fast Multiplication
10
5.8.2 Divide and Conquer Solution
5.8.2.1 Divide and Conquer
Assume n is a power of 2 for simplicity and numbers are in decimal.
5.8.2.2 Example
11