Translator's preface
This blog is actually a series of replies from Uncle Bob to xreborner (in the previous blog, xreborner proposed a series of sharp ideas for Uncle Bob to protect C ++ rights ).
Body
I compared the time consumption of C ++, Java, and Ruby in a recent blog. One of the participants (xreborner) submitted a convex hull algorithm code. It took me a long time to study the pitfalls. I did not find myself in the dark until I drew the algorithms on the graph.
The algorithm used by xreborner is like a Graham scan algorithm, whose complexity is at the O (nlogn) level. In fact, the algorithm mainly consumes time in fast sorting.
Another algorithm is Jarvis March, also known as giftwrapping. Its complexity is at the O (kN) level, and K represents the number of convex hull vertices.
I'm curious about which of the two algorithms will be faster. Obviously, O (nlogn) is faster than O (kN) only when logn <K. However, there is another consideration. Because Jarvis march is much simpler than Graham scan, I think the Jarvis March algorithm will be a better choice in most cases, the Graham algorithm is efficient only when the number of convex hull vertices is very large. So I compiled the Jarvis March algorithm and compared it with Graham scan of xreborner. The result is as follows:
Is it interesting? I constructed 1000 random points and drew a curve for the time and number of convex hull vertices. The intersection seems to be in the middle of the number of random convex hull vertices, and it is about 37! I guess it's a coincidence, but it's amazing.
Oh, as an exercise, check if you can understand Jarvis march and whether you can understand Graham scan.
Import java. util .*;
Public class jarvismarch {
Points PTS;
Private points hullpoints = NULL;
Private list <double> HY;
Private list <double> HX;
Private int startingpoint;
Private double currentangle;
Private Static final double max_angle = 4;
Public jarvismarch (points PTS ){
This. PTs = PTS;
}
/**
* The Jarvis march, sometimes known as the gift wrap algorithm.
* The next point is the point with the next largest angle.
* <P/>
* Imagine wrapping a string around a set of nails in a board. Tie the string to the leftmost nail
* And hold the string vertical. Now move the string clockwise until you hit the next, then the next, then
* The next. When the string is vertical again, you will have found the hull.
*/
Public int calculatehull (){
Initializehull ();
Startingpoint = getstartingpoint ();
Currentangle = 0;
Addtohull (startingpoint );
For (int p = getnextpoint (startingpoint); P! = Startingpoint; P = getnextpoint (p ))
Addtohull (P );
Buildhullpoints ();
Return hullpoints. X. length;
}
Public int getstartingpoint (){
Return pts. startingpoint ();
}
Private int getnextpoint (INT p ){
Double minangle = max_angle;
Int MinP = startingpoint;
For (INT I = 0; I <pts. X. length; I ++ ){
If (I! = P ){
Double thisangle = relativeangle (I, P );
If (thisangle> = currentangle & thisangle <= minangle ){
MinP = I;
Minangle = thisangle;
}
}
}
Currentangle = minangle;
Return MinP;
}
Private double relativeangle (int I, int p) {return false angle (PTS. X [I]-PTS. X [p], PTS. Y [I]-PTS. Y [p]);}
Private void initializehull (){
HX = new partition list <double> ();
Hy = new sort list <double> ();
}
Private void buildhullpoints (){
Double [] AX = new double [HX. Size ()];
Double [] Ay = new double [Hy. Size ()];
Int n = 0;
For (iterator <double> IX = HX. iterator (); ix. hasnext ();)
Ax [n ++] = IX. Next ();
N = 0;
For (iterator <double> Iy = Hy. iterator (); Iy. hasnext ();)
Ay [n ++] = Iy. Next ();
Hullpoints = new points (ax, ay );
}
Private void addtohull (INT p ){
HX. Add (PTS. X [p]);
Hy. Add (PTS. Y [p]);
}
/**
* The pseudo angle is a number that increases as the angle from vertical increases.
* The current implementation has the maximum pseudo do angle <4. The pseudo angle for each quadrant is 1.
* The algorithm is very simple. It just finds where the angle intesects a square and measures
* Perimeter of the square at that point. The math is in my Sept '06 notebook. unclebob.
*/
Public static double pseudo doangle (double dx, double Dy ){
If (dx> = 0 & dy> = 0)
Return quadrantonepseudoangle (dx, Dy );
If (dx> = 0 & dy <0)
Return 1 + quadrantonepseudoangle (math. Abs (dy), dx );
If (DX <0 & dy <0)
Return 2 + quadrantonepseudoangle (math. Abs (dx), math. Abs (dy ));
If (DX <0 & dy> = 0)
Return 3 + quadrantonepseudoangle (dy, math. Abs (dx ));
Throw new error ("impossible ");
}
Public static double quadrantonepseudoangle (double dx, double Dy ){
Return dx/(dy + dx );
}
Public points gethullpoints (){
Return hullpoints;
}
Public static class points {
Public Double X [];
Public Double Y [];
Public points (double [] X, double [] Y ){
This. x = X;
This. Y = y;
}
// The starting point is the point with the lowest x
// With ties going to the lowest Y. This guarantees
// That the next point over is clockwise.
Int startingpoint (){
Double miny = Y [0];
Double Minx = x [0];
Int Imin = 0;
For (INT I = 1; I <X. length; I ++ ){
If (X [I] <Minx ){
Minx = x [I];
Imin = I;
} Else if (Minx = x [I] & Y [I] <miny ){
Miny = Y [I];
Imin = I;
}
}
Return Imin;
}
}
}
(Original URL: http://www.butunclebob.com/articles.unclebob.convexhulltiming; Robert C. Martin's english blog: http://www.butunclebob.com/ArticleS.UncleBob)
Author profile: Robert C. Martin is the president of object mentor and a senior consultant in the field of Object design, pattern, UML, Agile Methodology and extreme programming. He is not only the author of Jolt's award-winning book Agile Software Development: Principles, models and practices (Chinese version) (Agile Software Development (English version, or the author of the bestselling book Designing object-oriented C ++ applications using the booch method. Martin is the editor of pattern development ages of Program Design 3 and more C ++ gems, and has co-authored XP in practice with James Newkirk. He is a well-known speaker at the international conference of programmers and has been an editor of C ++ report for four years.