Divide and Conquer law (II.)

Source: Internet
Author: User
Tags mul


Refer to the fourth chapter of algorithmic design and analysis Anany levitin translation version of Tsinghua University Press

In the previous article, we introduced the idea of divide-and-conquer strategy, the main theorem, and some classical cases with the method of divide and conquer. This article will continue to explore other applications of the divide-and-conquer algorithm, including large integer multiplication and Strassen matrix multiplication, the most recent point-to-problem and convex-hull problem of the 4 algorithms, generally not included in the regular data structure tutorial.

--------------------------------------------------------------------------------------------------------------- -----------------------------------



4.5 Large integer multiplication and Strassen matrix multiplication

Each of the two algorithms attempts to reduce the number of multiplication operations at the expense of adding a small amount of addition.



1) Large integer multiplication

The book tells you that the 2 digits are multiplied by the N-bit number, and I'll expand here and write a more general method, multiplying by 2 numbers of any number of digits:

Multiply A * b for 2 numbers of any number of digits, written as:

A = A1 * 10^ (N1/2) + a0-----N1 is a number of digits

b = B1 * 10^ (N2/2) + B0-----N2 is a bit of B

The divide-and-conquer strategy is based on the above transformation, a, B is written in the form of the first half of the number and the last half of the number, for example, if a = 5423678, then a1 = 542,a0 = 3678 (Note that if the even intercept is less than half)

So A and B can be multiplied to write: A * b = {A1 * 10^ (N1/2) + a0} * {B1 * 10^ (N2/2) + B0}

Expanded after finishing: A * b = a1*b1 * 10^[(N1+N2)/2] + a1*b0 * 10^ (N1/2) + a0*b1 * 10^ (N2/2) + a0*b0 four items

This makes it easy to recursively ask for a * b, and if you think the number is too large to decompose, you can continue to decompose . (You can specify when to end the recursion yourself)


Implementation method: We define a support method Mul (string s1,string s2), used to end recursion (in this case, I define a number is 1 bits when the end of recursion, directly with ordinary multiplication) to calculate the product of two strings (in order to represent a large number, the string to accept parameters). With this support method, the implementation of two large-number multiplication by splitting and administering recursion is as follows:

public static long mutiply (string a,string B)//read into 2 large integers with a string
{
Long result = 0;
if (a.length () = = 1 | | b.length () = = 1)//recursive end condition
result = Mul (A, b);
else//If the length of the 2 string is >= 2
{
String a1 = a.substring (0, A.length ()/2); Intercept the first half of the string (the shorter half)
String a0 = a.substring (A1.length (), a.length ()); Half the string after intercept
SYSTEM.OUT.PRINTLN (A1);
System.out.println (A0);
String B1 = b.substring (0, B.length ()/2);
String B0 = b.substring (B1.length (), b.length ());

The idea of division is to write integers like this: a = A1 * 10^ (N1/2) + a0, B = B1 * 10^ (N2/2), multiply expand to get the following four items
Where N1,n2 is a number of 2 integers A/b
result = (long) (Mutiply (A1,B1) * MATH.POW (Ten, A0.length () + b0.length ())
+ mutiply (a1,b0) * MATH.POW (Ten, A0.length ()) + mutiply (A0,B1) * MATH.POW (B0.length)
+ mutiply (a0,b0));
}

return result;
}



You can see that if we have 2 strings that are larger than 1 bits, then we continue to decompose A and B.

Complete code and test examples:

Bigintegermutiply

Package Section4;

Import Java.util.Arrays;


/* 4th Chapter Divide and conquer large integer multiplication--Calculate the product of 2 large integers */

public class Bigintegermutiply {
/**
* @param args
*/
public static void Main (string[] args) {
TODO auto-generated Method Stub
Long a = 95211154;
Long B = 9039;
String S1 = "95211154";
String s2 = "9039";

Long suppose = a * b;
Long result = Mutiply (S1,S2);

System.out.println (Suppose + "" + result);
System.out.println (Suppose = = result);

}

public static long mutiply (string a,string B)//read into 2 large integers with a string
{
Long result = 0;
if (a.length () = = 1 | | b.length () = = 1)//recursive end condition
result = Mul (A, b);
else//If the length of the 2 string is >= 2
{
String a1 = a.substring (0, A.length ()/2); Intercept the first half of the string (the shorter half)
String a0 = a.substring (A1.length (), a.length ()); Half the string after intercept
SYSTEM.OUT.PRINTLN (A1);
System.out.println (A0);
String B1 = b.substring (0, B.length ()/2);
String B0 = b.substring (B1.length (), b.length ());

The idea of division is to write integers like this: a = A1 * 10^ (N1/2) + a0, B = B1 * 10^ (N2/2), multiply expand to get the following four items
Where N1,n2 is a number of 2 integers A/b
result = (long) (Mutiply (A1,B1) * MATH.POW (Ten, A0.length () + b0.length ())
+ mutiply (a1,b0) * MATH.POW (Ten, A0.length ()) + mutiply (A0,B1) * MATH.POW (B0.length)
+ mutiply (a0,b0));
}

return result;
}

private static Long Mul (string s1,string s2) {//calculates the product of a large integer represented by 2 strings
This function is actually called only if the length of a string is 1 o'clock.

Int[] A = new int[s1.length ()]; The large integer s1, everyone.
Int[] B = new int[s2.length ()]; The large integer s2, everyone.

for (int i = 0;i < a.length;i++)//Convert the character ' I ' to an integer I into an array of integers
A[i] = (int) S1.charat (i)-48;
for (int i = 0;i < b.length;i++)
B[i] = (int) S2.charat (i)-48;

Long NUM1 = Tonum (a);
Long num2 = Tonum (b);

return NUM1 * NUM2;

}

private static Long Tonum (int[] a) {//Converts the bit array of an integer to its corresponding number
Long result = 0;
for (int i = 0;i < a.length;i++)
Result = result * + A[i];
SYSTEM.OUT.PRINTLN (result);
return result;
}


}



Product of 95211154 and 903923:

86063551957142 86063551957142
True

Note that this algorithm is only to see how the division is implemented, and my test case is not a large integer, the real large integer on the ordinary computer will overflow, the calculation does not come out. Some researchers have shown that, when multiplying by integers greater than 600 bits, the performance of the divide-and-conquer strategy exceeds the normal algorithm , and it is clear that such a large number cannot be computed on a conventional computer.


Expand a little, how does large integer addition work? ----------Large integer multiplication is designed to improve efficiency and reduce the number of multiplication operations, where the large integer addition is not an issue of efficiency, but a problem that the computer cannot represent. For example, for a 100-bit or even 1000-bit number, the computer's representation range is obviously unreachable, so you should use an array to hold every bit of a large integer, and for carry, directly forward one plus one.



2) Strassen matrix multiplication


Theoretical analysis is not much to write about, involving a lot of mathematical things, computational trouble, direct book on the diagram:

The divide-and-conquer strategy calculates 2 second-order matrices in the following ways:




which



A few times, so that the multiplication of 2 second-order matrices is calculated by 7 multiplication and 18 additions. The brute force method used 8 multiplication and 4 additions. Of course, this does not reflect its superiority, and its superiority is manifested in the asymptotic efficiency when the order of the matrix tends to be infinitely large.

By the above formula, according to the relevant knowledge of the Matrix, for the block matrix, can also be written in the above form:



The method of calculating matrix multiplication by divide-and-conquer strategy is as follows: (more support methods, see full code)

public static int[][] Strassenmul (int[][] a,int[][] b) {//a,b are 2 square squares
Int[][] result = new Int[a.length][a.length];
if (a.length = = 2)//If A and B are all 2-step, the recursive end condition
result = Strassmul (A, b);
else//otherwise (i.e., A and B are 4,8,16 ...)
{
Four sub-matrices of a
int[][] A00 = Copyarrays (a,1);
int[][] A01 = Copyarrays (a,2);
int[][] A10 = Copyarrays (a,3);
int[][] A11 = Copyarrays (a,4);
Four sub-matrices of b
int[][] B00 = Copyarrays (b,1);
int[][] B01 = Copyarrays (b,2);
int[][] B10 = Copyarrays (b,3);
int[][] B11 = Copyarrays (b,4);

Recursive invocation
Int[][] M1 = Strassenmul (Addarrays (A00,A11), Addarrays (B00,B11));
int[][] m2 = Strassenmul (Addarrays (A10,A11), B00);
int[][] m3 = Strassenmul (A00,subarrays (B01,B11));
Int[][] M4 = Strassenmul (A11,subarrays (b10,b00));
Int[][] M5 = Strassenmul (Addarrays (A00,A01), B11);
Int[][] M6 = Strassenmul (Subarrays (a10,a00), Addarrays (B00,B01));
int[][] M7 = Strassenmul (Subarrays (A01,A11), Addarrays (B10,B11));

Get four sub-matrices of result
int[][] C00 = Addarrays (M7,subarrays (Addarrays (M1,M4), M5));//m1+m4-m5+m7
int[][] C01 = Addarrays (M3,M5); M3+m5
int[][] C10 = Addarrays (M2,M4); M2+m4
int[][] C11 = Addarrays (M6,subarrays (Addarrays (m1,m3), m2));//m1+m3-m2+m6

You can also use the following methods to find C
C00 = Addarrays (Strassenmul (a00,b00), Strassenmul (A01,B10));
C01 = Addarrays (Strassenmul (A00,B01), Strassenmul (A01,B11));
C10 = Addarrays (Strassenmul (a10,b00), Strassenmul (A11,B10));
C11 = Addarrays (Strassenmul (A10,B01), Strassenmul (A11,B11));

Merging four sub-matrices into result
Merge (result,c00,1);
Merge (result,c01,2);
Merge (result,c10,3);
Merge (result,c11,4);
}
return result;
}



Full code:

Strassen



for the given 2 matrices, the product output is as follows:

Output matrix:
5  4  7  3  
4  5  1  9   
8  1  3  7  
5  8  7  7  

Of course, the scale of the matrix is larger to show its advantages.


Hint: In fact, the second-order time to end recursion direct brute force on the line, and you can at the end of the four-stage recursion can also be done, depending on your implementation


-------------------------------------- ------------------------------------------------------------------------------------------------------------



4.6 To solve the most recent point-to-problem and convex-hull problems by using the divide-and-conquer method


The description of the problem is introduced in the brute force method and is no longer introduced.

1) Nearest point to question

in pretty Lifari wrote a 2-cycle brute force algorithm, the time complexity is n^2.


A, pre-order!

B, on a pre-order basis, divides the points recursively into half-left.

Implementation: Ascending order of the point set by x-coordinate ascending. The set of points is then recursively divided into left and right halves, with the brute force method ending recursion when there are only 2 or 3 points left in the point set.

Pay attention to the policy of split:

Sub-problem: Divide the problem scale recursively into 2 halves

The solution of the merge sub-problem: The last 2 points of the 2 halves are calculated separately, assuming that the left half is point A, point B, and the right is dot C, point D. Then take 2 of its smaller distance as the temporary minimum . Note that at this point ab or CD is not the final requirement of 2 points, the minimum value may also come from a point in the left subset of a point in the right child set. (at 2 points on the left and right subset split boundary, one in a subset, and one in the subsets)

The Main method is as follows: The method accepts an array of points, which returns the nearest 2 points.

public static point[] Getnearestpoints (point[] Points) {
Find the nearest two points from a group of points and return to these two points
Point[] result = new POINT[2];
if (points.length = = 3 | | Points.length = = 2)//recursion End condition
result = Getnear (Points);
else//More than 3 points, divide and conquer, find the nearest pair of two sub-sets, and then merge the results
{
Point[] left = Arrays.copyofrange (Points, 0, POINTS.LENGTH/2);//The last subscript does not include
Point[] right = Arrays.copyofrange (Points, POINTS.LENGTH/2,
Points.length);

Get 2 points of the minimum distance within 2 subsets
point[] Result1 = getnearestpoints (left);
point[] result2 = getnearestpoints (right);

Double D1 = dpoints (result1[0], result1[1]);
Double D2 = dpoints (Result2[0], result2[1]);

Forgot to assign result value
if (D1 <= D2)
result = RESULT1;
Else
result = RESULT2;

Merge results: Find two points with the shortest global distance
Double dmin = math.min (d1, D2);

int x1 = left.length-1;//two x cutoff point
int x2 = x1 + 1;
Is wrong when POINTS.LENGTH/2 is an integer
int x1 = POINTS[POINTS.LENGTH/2-1].x;//two x cutoff point
int x2 = points[points.length/2].x;

for (int i = x1; I >= 0; i--) {
if (x2-points[i].x > DMin)//Direct commissioning for a long time do not know where the wrong!!!!!!
if (points[x2].x-points[i].x > DMin)
Break
Else
for (int j = Points.length/2;j < points.length;j++)
for (int j = x2; J < Points.length; J + +) {
System.out.println (POINTS[J].Y);
if (Points[j].x-x1 > DMin)
if (points[j].x-points[x1].x > DMin)
Break
else {
Double temp = dpoints (Points[i], points[j]);
SYSTEM.OUT.PRINTLN (temp);
if (Temp < DMin) {
DMin = temp;
Result[0] = points[i];
RESULT[1] = Points[j];
}
}

}
}
}

return result;
}



Note that you want to get the values of the left and right borders, and then expand from the left and right edges to see if there are any smaller values. At first, this place made a mistake, see note.

See the full code for additional support methods:

Nearestpoint


The two closest points to the output distance are:
(7,8) (7,9)

The recursive formula for this algorithm is:


According to the main theorem, the time complexity is n * log (n), note that this is only a recursive solution for dividing and merging sub-problems. There is also a pre-ordering, because the pre-ordering time is also a complex n * log (n), it does not affect the overall complexity.


Hint: This example tells us that sometimes the solution of a merge sub-problem is not simply a combination of the solution of the problem, it needs some further processing. The final solution may not be in the solution of the sub-problem, but it comes from the solution of the pair problem further processing results.



2) Convex packet problem

The description is more complex, simply put, first pre-ordered, pre-sorted the leftmost and most right point is definitely the point in the convex hull. The convex hull can then be recursively extended from the inside out, looking for the highest point on the 2 sides of the current line, and the highest point must be in the convex hull, where some mathematical knowledge is involved:

A, first define the Ray P1 to the left of P2: if the order of P1 P2 p is counterclockwise, it is called p on the left side of the Ray

b, the area of the triangular P1 P2 P3 is equal to half of the following determinant:



This value is positive only if the P3 is on the left side of the Ray P1p2.

Thus it is easy to find the highest point on the left side of the P1,P2 (the point farthest from the line, which is the new vertex that the convex hull expands outwards), and when the highest point is obtained, 2 new edges are obtained, and the expansion continues outward.




Note I only define the left side, in fact, around the same, you change the direction of the ray, processing the right side is equal to the left side of the deal, so as not to make so much trouble. See the code:

Package Section4;


/* Part 4th the solution to the convex hull problem of division and Treatment method */

Import Java.util.Arrays;
Import Java.util.Iterator;
Import java.util.LinkedList;
Import Java.util.Stack;

public class Convexhull {

/**
* @param args
*/
public static void Main (string[] args) {
TODO auto-generated Method Stub
point[] Points = new POINT[15];

Points[0] = new Point (-5, 7);
POINTS[1] = new Point (3,-6);
POINTS[2] = new Point (5, 4);
POINTS[3] = new Point (-5,-5);
POINTS[4] = new Point (1, 7);
POINTS[5] = new Point (6, 0);

POINTS[6] = new Point (0, 0);
POINTS[7] = new Point (-5, 0);
POINTS[8] = new Point (3,-2);
POINTS[9] = new Point (3, 4);

POINTS[10] = new Point (1, 6);
POINTS[11] = new Point (5, 3);
POINTS[12] = new Point (-4,-5);
POINTS[13] = new Point (-3, 6);
POINTS[14] = new Point (2, 5);

Arrays.sort (Points); Pre-sorting processing

linkedlist<point> list = new linkedlist<point> ();
for (int i = 0;i < points.length;i++)
List.add (Points[i]); List holds all the vertices

linkedlist<point> result = Getconvexhulls (list); Result is used to hold the final result vertex

System.out.println ("A total of" + result.size () + "vertices," + "convex hull vertices are:");
Iterator it = Result.iterator ();
while (It.hasnext ())
{
Point next = (point) it.next ();
System.out.print ("(" + Next.x + "," + Next.y + ")" + "");
}
}

public static linkedlist<point> Getconvexhulls (linkedlist<point> list) {
Returns the convex hull vertex as a result linked list
linkedlist<point> result = new linkedlist<point> ();

Point temp1 = List.removefirst ();
Point temp2 = List.removelast ();
Result.add (TEMP1);
Result.add (TEMP2);

Recursive processing Temp1---> temp2 points on both sides
Dealwithleft (temp1,temp2,result,list);
Dealwithleft (temp2,temp1,result,list);//Pay attention to each time you want to bring results with you to store the result set

return result;
}

private static void Dealwithleft (point p1,point p2,linkedlist result,linkedlist list) {
Recursive processing p1,p2 the left point of the ray composition
Iterator it = List.iterator ();

Find the highest point on the left Pmax
Point Pmax = null;
int max = 0;
while (It.hasnext ())
{
Point next = (point) it.next ();
int x1 = P1.x,y1 = p1.y;
int x2 = P2.x,y2 = p2.y;
int x3 = Next.x,y3 = Next.y;

int max = 0;//A little mistake,!!!!!!!.
int compute = X1*y2 + x3*y1 + x2*y3-x3*y2-x2*y1-x1*y3;
if (Compute > Max)
{
max = COMPUTE;
Pmax = Next;
}
}

and found a vertex.
if (Pmax! = null)
{
Result.add (Pmax);
List.remove (Pmax);

Recursive
Dealwithleft (p1,pmax,result,list);
Dealwithleft (pmax,p2,result,list);
}
}

private static Boolean Onleft (Point target,point p1,point p2) {
Determine if target is on the left side of the P1--->p2 ray
int x1 = P1.x,y1 = p1.y;
int x2 = P2.x,y2 = p2.y;
int x3 = Target.x,y3 = Target.y;

int compute = X1*y2 + x3*y1 + x2*y3-x3*y2-x2*y1-x1*y3;
if (Compute > 0)
return true;
Else
return false;
}


}


The 14 points of the above test are carefully constructed with 14 points of various special cases (including 3-point collinear, a bit of the horizontal axis, etc.), the results of the operation are as follows, interested can be drawn in the coordinate system.

There are 6 vertices, the vertices of the convex hull are:
( -5,7) (6,0) (1,7) (5,4) ( -5,-5) (3,-6)


Hint: This code is not good to write, in fact, every time you judge one side, you can remove the other side of the point, here has not done so.



--------------------------------------------------------------------------------------------------------------- -----------------------------------


Summarize:

The strategy of divide and conquer

How to divide the sub-problem

How to use recursion

When the scale is small, the recursion ends.

How to merge Results

Divide and Conquer law (II.)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.