CMSC 435 Algorithm Analysis
& Design
Divide and Conquer
Example 1. Multiplication of polynomials of the same degree
Let P(x) and Q(x) be two polynomials of degree n - 1:
P(x) = an-1xn-1 + an-2xn-2 + … + a1x + a0 and
Q(x) = bn-1xn-1
+ bn-2xn-2 + … + b1x + b0
Let d = én/2ù
We can write the two polynomials as
P(x) = xdP2(x) + P1(x)
Q(x) = xdQ2(x) + Q1(x)
where
P2(x) = an-1xn-d-1 + an-2xn-d-2 + … + ad+1x + ad and
P1(x)
= ad-1xd-1 + … + a1x + a0
and similarly for Q1 and Q2
a straightforward application of the distributive law of multiplication yields:
P(x)Q(x) = x2dP2(x)Q2(x) + xd(P2(x)Q1(x)) + P1(x)Q2(x)) + P1(x)Q1(x)
There are four products: P2Q2, P2Q1, P1Q2, and P1Q1
each of these products is an n/2 polynomial x n/2 polynomial = n2/4 products of the coefficients. The total number of multiplications is thus 4(n2/4) + Q(n).
But this is the same number of multiplications that was needed to multiply the two polynomials without dividing them into two parts each. We have divided, but we have not conquered!
However, there is a less intuitive, but more insightful way in which we can combine the split polynomials that uses only three multiplications instead of four.
P(x)Q(x) = x2dP2(x)Q2(x) + xd(P1(x) + P2(x))(Q1(x) + Q2(x))
- P1(x)Q1(x) - P2(x)Q2(x)) + P1(x)Q1(x)
Only three products of polynomials are needed:
P1(x)Q1(x), P2(x)Q2(x), and (P1(x) + P2(x))(Q1(x) + Q2(x))
We can then write the (pseudocode) divide and conquer algorithm as
function polyMult(P(x), Q(x), n) {
if (n = 1)
return a0 * b0;
else {
d = n/2 + n%2;
split(P(x), P2(x), P1(x)); //split P into two halves-- P1 and P2
split(Q(x), Q2(x), Q1(x)); //split Q into two halves
R(x) = polyMult(P2(x), Q2(x), d);
S(x) = polyMult(P1(x) + P2(x), Q1(x) + Q2(x), d);
T(x) = polyMult(P1(x), Q1(x), d);
return x2d * R(x) + xd (S(x) - R(x) - T(x)) + T(x);
}
}
Let T(n) = the number of coefficient multiplications and additions. Then:
T(n) = 3T(n/2) + cn2 if n > 1 and T(n) = 1 if n = 1
from the Master Theorem we obtain: T(n) = Q(nlg(3))
To illustrate this algorithm, consider the following problem:
P(x) = x3 + 2x2 + 3x + 4
Q(x) = 5x3 - 4x2 - 2x + 1
Multiplying these two polynomials in the usual way we obtain:
P(x)Q(x) = 5x6 + 6x5 + 5x4 + 5x3 - 20x2 - 5x + 4
Now let us apply the algorithm to the same problem.
product = polyMult([1,2,3,4],[5,-4,-2,1],4)
This first call to polyMult leads to the following sequence of recursive calls:
R1(x) = polyMult([1,2],[5,-4],2)
S1(x) = polyMult([1+3, 2+4], [5-2, -4+1],2) = polyMult([4,6], [3, -3],2)
T1(x) = polyMult([3,4],[-2,1],2)
Each of these calls to polyMult produces the following:
To find R1(x), where the subscript indicates this is the value of R(x) for the original call to polyMult, we have:
R2(x) = polyMult([1],[5],1) = 5 // from the if n == 1 block
S2(x) = polyMult([1+2],[5-4],1) = 3
T2(x) = polyMult([2],[-4],1) = -8
R1ß return R2(x)x2 + (S2(x) - R2(x) - T2(x))x + T2(x) = 5x2 + 6x - 8
Similarly, to find S1(x) we have polyMult([4,6],[3,-3],2)
R2(x) = polyMult([4],[3],1) = 12 // from the if n == 1 block
S2(x) = polyMult([4+6],[3-3],1) = 0
T2(x) = polyMult([6],[-3],1) = -18
S1ß return R2(x)x2 + (S2(x) - R2(x) - T2(x))x + T2(x) = 12x2 + 6x - 18
and, to find T1(x) we have polyMult([3,4],[-2,1],2)
R2(x) = polyMult([3],[-2],1) = -6 // from the if n == 1 block
S2(x) = polyMult([3+4],[-2+1],1) = -7
T2(x) = polyMult([4],[1],1) = 4
T1ß return R2(x)x2 + (S2(x) - R2(x) - T2(x))x + T2(x) = -6x2 - 5x + 4
Now given R1, S1, and T1, we are ready to return the product.
PQ = R1(x)x4 + (S1(x) - R1(x) - T1(x))x2 + T1(x)
= (5x2 +6x-8)x4 + (12x2+6x-18)x2 -(5x2 +6x-8)x2 -(-6x2-5x+4)x2 - 6x2 - 5x + 4
= 5x6 +6x5 + (-8 + 12 - 5 +6)x4 + (6 - 6 +5)x3 + (-18 + 8 - 4 -6)x2 - 5x + 4
= 5x6 + 6x5 + 5x4 + 5x3 - 20x2 - 5x + 4
Here is an implementation of the previous pseudocode algorithm. The polynomials are represented by vectors of the coefficients. The coefficients of the higher power terms are in the lower indices of the array. The powers of x are implicit.
public double [ ] polyMult(double [ ] p, double [ ] q, int n) {
if (n == 1) {
double [ ] temp = {p[0] * q[0]};
return temp;
}
int d = n/2 + n%2;
//split p and q
double [ ] p2 = new double[d];
double [ ] q2 = new double [d];
for (int i = 0; i < d; i++) {
p2[i] = p[i];
q2[i] = q[i];
}
double [ ] p1 = new double[d-n%2];
double [ ] q1 = new double[d-n%2];
for (int i = 0; i < d; i++) {
p1[i] = p[i+d];
q1[i] = q[i+d];
}
double [ ] r = polyMult(p2,q2,d);
double [ ] s = polyMult(p2+p1, q2+q1,d);
double [ ] t = polyMult(p1,q1,d);
double [ ] prod = new double[2 * n];
//remember, the lowest index holds the coefficient of the highest power term
//multiplying by xd involves shifting d places to the left which is equivalent
//to shifting the terms multiplied by a lower power d places to the right
for (int i = 0; i < d; i++) {
prod[i] = r[i];
prod[i+d] += s[i] - r[i] - t[i];
prod[i+2*d] += t[i];
}
return prod;
}
Suppose Q(x) is of degree n-1 and P(x) of degree m-1 where m < n.
Assume for convenience that n = km for some positive integer k.
Q(x) = Qk(x)xm(k-1) + Qk-1(x)xm(k-2) + … + Q2(x) + Q1(x)
It follows then that
P(x)Q(x) = P(x) Qk(x)xm(k-1) + P(x) Qk-1(x)xm(k-2) + … + P(x)Q1(x)
function polyMultUnequal(P(x), Q(x), n, m) {
prodPoly(x) = 0;
for (int i = 1; i <= k; i++)
Qi(x) = bi(m-1)xm-1 + bi(m-1)xm-2 + … + b(i-1)m;
for (int i = 1; i <= k; i++)
prodPoly(x) += x(i-1)m polyMult(P(x), Qi(x), m);
return prodPoly(x);
}
The complexity of this Algorithm is Q(kmlg3) = Q((n/m) mlg3) = Q(n mlg(3/2))
Example 2 -- Strassen's Algorithm for matrix Multiplication
Let A and B be two NxN matrices.
Divide A and B into 4 (N/2 x N/2) submatrices
|A11 A12| |B11 B12|
A = | | B = | |
|A21 A22| |B21 B22|
The usual matrix multiplication requires 8 products of the submatrices:
A11B11 A12B21 A11B12 A12B22
A21B11 A22B21 A21B12 A22B22
Strassen found a clever, non-intuitive way to carry out matrix multiplication using only 7 matrix multiplications.
M1 = (A11 + A22)(B11 + B22)
M2 = (A21 + A22)B11
M3 = A11(B12 - B22)
M4 = A22(B21 - B11)
M5 = (A11 + A12)B22
M6 = (A21 - A11)(B11 + B12)
M7 = (A12 - A22)(B21 + B22)
and
|M1 + M4 - M5 + M7 M3 + M5 |
AB = | |
|M2 + M4 M1 + M3 - M2 + M6 |
Let T(n) = the number of multiplications for the product of two n by n matrices.
Then
1 if n = 1
T(n) =
7T(n/2) + 18n2 if n > 1
From the Master Theorem, T(n) = Q(nlg(7)) = Q(n2.81)
Example
3. Tiling With Triominoes
A tromino is an object composed of three 1 x 1 squares. Trominoes can appear in four different orientations:

These orientations are labeled UL -- upper left;
The problem we are to solve is to tile with trominoes an N x N board with one deficiency, where N is 2m for m > 1. A deficiency is where one of the unit squares in the board has been "removed".

To show that such a tiling is feasible, we first need to prove that the number of non-deficient unit squares is divisible by three. (Since a tromino covers a set of three squares.)
Exercise: Prove by induction that the number of non-deficient squares is divisible by three.
Hint: The number of non-deficient square = N2 - 1 = 22m -1 = 4m - 1
This can be written as 4(4m-1) - 4 + 3 = 4(4m-1 - 1) + 3
This last term is divisible if (4m-1 - 1) is divisible. Now develop the inductive
proof.
We will employ a divide and conquer strategy to solve this problem.
Divide the board into four quadrants. The deficiency will be located in one of the quadrants.

Our approach is to divide the board into four quadrants, locate the quadrant with the deficiency, place a tromino with the appropriate orientation at the corner of the quadrant with the deficiency, mark the three squares covered by the tromino in the three remaining quadrants as deficient, and recursively tile the four quadrants.

In the following algorithm we will locate the board to be tiled and the deficient square by the coordinates of their lower left corner.
public void tile(int n, Pt pb, Pt, pd) {
if (n == 2) {
String str; //identify the orientation of the tromino
if (pb.equals(pd) )
str
= "
else if (pb.getY( ) == pd.getY( ))
str = "UL";
else if (pb.getX( ) == pd.getX( ))
str = "LR";
else
str = "LL";
place(str, pb.getX( ) + 1, pb.getY( ) + 1);
}
else {
//partition into 4 quadrants and locate the quadrant with the deficiency
Pt p = new Pt(pb.getX( ) + n/2, pb.getY( ) + n/2);
String str; //determine the orientation of the tromino
if (pd.getX( ) <= p.getX( ) && pd.getY( ) >= p.getY( )) {
//deficiency is in upper left quadrant
str = "LR";
tile(n/2, new Pt(pb.getX( ), p.getY( )), new Pt(pd)); //upper left
tile(n/2, new Pt(p), new Pt(p)); //upper right
tile(n/2, new Pt(pb), new Pt(p.getX( ) - 1, p.getY( ) -1)); //lower left
tile(n/2, new Pt(p.getX( ), pb.getY( )), new Pt(p.getX( ), p.getY( ) - 1);
}
else if (pd.getX( ) >= p.getX( ) && pd.getY( ) >= p.getY( )) {
//deficiency is in upper right quadrant
str = "LL";
//upper left
tile(n/2, new Pt(pb.getX( ), p.getY( )), new Pt(p.getX( )-1, p.getY( ));
tile(n/2, new Pt(p), new Pt(pd)); //upper right
tile(n/2, new Pt(pb), new Pt(p.getX( ) - 1, p.getY( ) -1)); //lower left
tile(n/2, new Pt(p.getX( ), pb.getY( )), new Pt(p.getX( ), p.getY( ) - 1);
}
else if (pd.getX( ) <= p.getX( ) && pd.getY( ) <= p.getY( ))
//deficiency is in lower left quadrant
str = "
//upper left
tile(n/2, new Pt(pb.getX( ), p.getY( )), new Pt(p.getX( )-1, p.getY( ));
tile(n/2, new Pt(p), new Pt(p)); //upper right
tile(n/2, new Pt(pb), new Pt(pd)); //lower left
tile(n/2, new Pt(p.getX( ), pb.getY( )), new Pt(p.getX( ), p.getY( ) - 1);
}
else
//deficiency is in lower right quadrant
str = "UL";
//upper left
tile(n/2, new Pt(pb.getX( ), p.getY( )), new Pt(p.getX( )-1, p.getY( ));
tile(n/2, new Pt(p), new Pt(p)); //upper right
tile(n/2, new Pt(pb), new Pt(p.getX( ) - 1, p.getY( ) -1)); //lower left
tile(n/2, new Pt(p.getX( ), pb.getY( )), new Pt(pd);
}
place(str, p.getX( ), p.getY( ));
}
}