平面点集凸壳算法集合及其复杂度比较

326 篇文章 2 订阅
20 篇文章 0 订阅

转载自:http://geomalgorithms.com/a10-_hull-1.html


Computing a convex hull (or just "hull") is one of the first sophisticated geometry algorithms, and there are many variations of it. The most common form of this algorithm involves determining the smallest convex set (called the "convex hull") containing a discrete set of points. This algorithm also applies to a polygon, or just any set of line segments, whose hull is the same as the hull of its vertex point set. There are numerous applications for convex hulls: collision avoidance, hidden object determination, and shape analysis to name a few. And they are a minimal linear bounding container.

The most popular hull algorithms are the "Graham scan" algorithm [Graham, 1972] and the "divide-and-conquer" algorithm [Preparata & Hong, 1977]. Implementations of both these algorithms are readily available (see [O'Rourke, 1998]). Both areO(nlog-n) time algorithms, but the Graham has a low runtime constant in 2D and runs very fast there. However, the Graham algorithm does not generalize to 3D and higher dimensions whereas the divide-and-conquer algorithm has a natural extension. We do not consider 3D algorithms here (see [O'Rourke, 1998] for more information).

Here is a list of some well-known 2D hull algorithms. Let n = # points in the input set(翻译:输入点集数量), andh = # vertices on the output hull(翻译:h表示凸壳点数). Note that h.le.n, sonh.le.n2. The list is ordered by date of first publication.

Algorithm

Speed

Discovered By

Brute Force

O(n4)

[Anon, the dark ages]

Gift Wrapping

O(nh)

[Chand & Kapur, 1970]

Graham Scan

O(nlog-n)

[Graham, 1972]

Jarvis March

O(nh)

[Jarvis, 1973]

QuickHull

O(nh)

[Eddy, 1977], [Bykat, 1978]

Divide-and-Conquer

O(nlog-n)

[Preparata & Hong, 1977]

Monotone Chain

O(nlog-n)

[Andrew, 1979]

Incremental

O(nlog-n)

[Kallay, 1984]

Marriage-before-Conquest

O(nlog-h)

[Kirkpatrick & Seidel, 1986]注意这个方法

注解:不同来源数据(http://www.tcs.fudan.edu.cn/rudolf/Courses/Algorithms/Alg_cs_07w/Webprojects/Zhaobo_hull/#section29)对各种方法复杂度统计不一致
Convex Hulls

The convex hull of a geometric object (such as a point set or a polygon) is the smallest convex set containing that object. There are many equivalent definitions for a convex setS. The most basic of these is:

Def 1. A set S is convex if whenever two points P andQ are inside S, then the whole line segmentPQ is also in S.

Pic_convex1

But this definition does not readily lead to algorithms for constructing convex sets. A more useful definition states:

Def 2. A set S is convex if it is exactly equal to the intersection of all the half planes containing it.

It can be shown that these two definitions are equivalent. However, the second one gives us a better computational handle, especially when the setS is the intersection of a finite number of half planes. In this case, the boundary ofS is polygon in 2D, and polyhedron in 3D, with which it can be identified.

Def 3. The convex hull of a finite point setS = {P} is the smallest 2D convex polygonOMEGA (or polyhedron in 3D) that containsS. That is, there is no other convex polygon (or polyhedron) LAMBDA withS.in.LAMBDA.in.OMEGA.

Also, this convex hull has the smallest area and the smallest perimeter of all convex polygons that containS.


2D Hull Algorithms

For this algorithm we will cover two similar fast 2D hull algorithms: the Graham scan, and Andrew's Monotone Chain scan. They both use a similar idea, and are implemented as a stack. In practice, they are both very fast, but Andrew's algorithm will execute slightly faster since its sort comparisons and rejection tests are more efficient. An implementation of Andrew's algorithm is given below in ourchainHull_2D() routine.


The "Graham Scan" Algorithm(评注:经典方法——要计算极角并排序)

The Graham scan algorithm [Graham, 1972] is often cited ([Preparata & Shamos, 1985], [O'Rourke, 1998]) as the first real "computational geometry" algorithm. As the size of the geometric problem (namely,n = the number of points in the set) increases, it achieves the optimal asymptotic efficiency ofO(nlog-n) time. This algorithm and its implementation has been covered in great detail by [O'Rourke, 1998, Sect 3.5, 72-86] with downloadable C code available from his web site: Computational Geometry in C. We do not repeat that level of detail here, and only give a conceptual overview of the algorithm. TheO(nlog-n) time for the Graham scan is spent doing an initial radial sort of the input set points. After that, the algorithm employs a stack-based method which runs in just O(n) time. In fact, the method performs at most 2n simple stack push and pop operations. Thus, it executes very rapidly, bounded only by the speed of sorting.

Let S = {P} be a finite set of points. The algorithm starts by picking a point inS known to be a vertex of the convex hull. This can be done in O(n) time by selecting the rightmost lowest point in the set; that is, a point with first a minimum (lowest)y coordinate, and second a maximum (rightmost) x coordinate. Call this base pointP0. Then, the algorithm sorts the other pointsP in S radially by the increasing counter-clockwise (ccw) angle the line segmentP0P makes with thex-axis. If there is a tie and two points have the same angle, discard the one that is closest toP0. For efficiency, it is important to note that the sort comparison between two pointsP1 andP2 can be made without actually computing their angles. In fact, computing angles would use slow inaccurate trigonometry functions, and doing these computations would be a bad mistake. Instead, one just observes that P2 would make a greater angle thanP1 if (and only if)P2 lies on the left side of the directed line segmentP0P1 as shown in the following diagram. This condition can be tested by a fast accurate computation that uses only 5 additions and 2 multiplications. The code for this test was given in theisLeft() routine from Algorithm 1 about theArea of Triangles and Polygons.

Pic_graham1

After sorting, let the ccw-radially-ordered point set beS={P0,P1,...,Pn-1}. It looks like a fan with a pivot at the pointP0.

Pic_graham2

We next loop through the points of S one-by-one testing for convex hull vertices. The algorithm is an inductive incremental procedure using a stack of points. At each stage, we save (on the stack) the vertex points for the convex hull of all points already processed. This is the induction condition. We start withP0 andP1 on the stack. Then at thek-th stage, we add the next point Pk, and compute how it alters the prior convex hull. Because of the wayS was sorted, Pk is outside the hull of the prior pointsPi withi < k, and it must be added as a new hull vertex on the stack. But it's addition may cause previous stack points to no longer be a hull vertices. If this happens, the previous points must be popped off the stack and discarded. One tests for this by checking if the new point Pk is to the left or the right of the line joining the top two points of the stack. Again, we use the routineisLeft() to quickly make this test. IfPk is on the left of the top segment, then prior hull vertices remain intact, andPk gets pushed onto the stack. But, if it is on the right side of the top segment, then the prior point at the stack top will get absorbed inside the new hull, and that prior point must be popped off the stack. This test against the line segment at the stack top continues until eitherPk is left of that line or the stack is reduced to the single base pointP0. In either case,Pk gets pushed onto the stack, and the algorithm proceeds to the next pointPk+1 in the set. The different possibilities involved are illustrated in the following diagram.

Pic_graham3

It is easy to understand why this works by viewing it as an incremental algorithm. The old stackSk-1={P0,...,Pt2,Pk-1}, withPk–1 at the top, is the convex hull of all pointsPi with i< k. The next point Pk is outside this hull since it is left of the lineP0Pk–1 which is an edge of theSk–1 hull. To incrementally extendSk–1 to includePk, we need to find the two tangents fromPk toSk–1. One tangent is clearly the linePkP0. The other is a linePkPt such thatPk is left of the segment inSk–1 precedingPt and is right of the segment followingPt (when it exists). This uniquely characterizes the second tangent sinceSk–1 is a convex polygon. The way to findPtis simply to search from the top of the stack down until the point with the property is found. The points abovePt inSk–1 are easily seen to be contained inside the triangleDELTA-P0PtPk, and are thus no longer on the hull extended to includePk. So, they can be discarded by popping them off the stack during the search forPt. Then, thek-th convex hull is the new stack Sk={P0,...,Pt,Pk}.

At the end, when k = n-1, the points remaining on the stack are precisely the ordered vertices of the convex hull's polygon boundary. Note that for each point ofS there is one push and at most one pop operation, giving at most 2n stack operations for the whole algorithm.

This procedure is summarized by the following pseudo-code.

Pseudo-Code: Graham Scan Algorithm

   Input: a  set of points S = {P = (P.x,P.y)}

    Select the rightmost lowest point P0 in S
    Sort S radially (ccw) about P0 as a center {
        Use isLeft() comparisons
        For ties, discard the closer points
    }
    Let P[N] be the sorted array of points with P[0]=P0

    Push P[0] and P[1] onto a stack OMEGA

    while i < N
    {
        Let PT1 = the top point on OMEGA
        If (PT1 == P[0]) {
            Push P[i] onto OMEGA
            i++     // increment i
        }
        Let PT2 = the second top point on OMEGA
        If (P[i] is strictly left of the line  PT2 to PT1) {
            Push P[i] onto OMEGA
            i++     // increment i
        }
        else
            Pop the top point PT1 off the stack
    }

    Output: OMEGA = the convex hull ofS.

 


Andrew's Monotone Chain Algorithm

[Andrew, 1979] discovered an alternative to the Graham scan that uses a linear lexographic sort of the point set by thex and y-coordinates. This is an advantage if this ordering is already known for a set, which is sometimes the case. But even if sorting is required, this is a faster sort than the angular Graham-scan sort with its more complicated comparison function. The "Monotone Chain" algorithm computes the upper and lower hulls of a monotone chain of points, which is why we refer to it as the "Monotone Chain" algorithm. Like the Graham scan, it runs inO(nlog-n)(注意) time due to the sort time. After that, it only takesO(n) time to compute the hull. This algorithm also uses a stack in a manner very similar to Graham's algorithm.

First the algorithm sorts the point set S={P0,P1,...,Pn-1} by increasing x and then y coordinate values. Let the minimum and maximumx-coordinates be xmin andxmax. Clearly, P0.x=x-min, but there may be other points with this minimum x-coordinate. Let Pminmin be a point inS with P.x=x-min first and then miny among all those points. Also, let Pminmax be the point withP.x=x-min first and then maxy second. Note that Pminmin=Pminmaxwhen there is a unique x-minimum point. Similarly define Pmaxmin andPmaxmax as the points withP.x=x-max first, and theny min or max second. Again note that Pmaxmin=Pmaxmaxwhen there is a unique x-maximum point. Next, join the lower two points,Pminmin andPmaxmin to define a lower lineL-min. Also, join the upper two points,Pminmax andPmaxmax to define an upper lineL-max. These points and lines are shown in the following example diagram.

Pic_chain

The algorithm now proceeds to construct a lower convex vertex chainOMEGA-min belowL-min and joining the two lower pointsPminmin andPmaxmin; and also an upper convex vertex chainOMEGA-max aboveL-max and joining the two upper pointsPmaxmax andPminmax. Then the convex hullOMEGA ofS is constructed by joining OMEGA-min andOMEGA-max together.

The lower or upper convex chain is constructed using a stack algorithm almost identical to the one used for the Graham scan. For the lower chain, start withPminmin on the stack. Then process the points ofS in sequence. For OMEGA-min, only consider points strictly below the lower lineL-min. Suppose that at any stage, the points on the stack are the convex hull of points belowL-min that have already been processed. Now consider the next pointPk that is belowL-min. If the stack contains only the one pointPminmin then putPk onto the stack and proceed to the next stage. Otherwise, determine whether Pk is strictly left of the line between the top two points on the stack. If it is, put Pk onto the stack and proceed. If it is not, pop the top point off the stack, and testPk against the stack again. Continue untilPk gets pushed onto the stack. After this stage, the stack again contains the vertices of the lower hull for the points already considered. The geometric rationale is exactly the same as for the Graham scan. After all points have been processed, pushPmaxmin onto the stack to complete the lower convex chain.

The upper convex chain OMEGA-max is constructed in an analogous manner. But, process S in decreasing order {Pn-1,Pn-2,...,P0}, starting at Pmaxmax, and only considering points aboveL-max. Once the two hull chains have been found, it is easy to join them together (but be careful to avoid duplicating the endpoints).


Pseudo-Code: Andrew's Monotone Chain Algorithm

   Input: a set S = {P = (P.x,P.y)} of N points

    Sort S by increasing x and then y-coordinate.
    Let P[] be the sorted array of N points.

    Get the points with 1st x min or max and 2nd y min or max
        minmin = index of P with min x first and min y second
        minmax = index of P with min x first and max y second
        maxmin = index of P with max x first and min y second
        maxmax = index of P with max x first and max y second

    Compute the lower hull stack as follows:
    (1) Let L_min be the lower line joining P[minmin] with  P[maxmin].
    (2) Push P[minmin] onto the stack.
    (3) for i = minmax+1 to maxmin-1 (the points between xmin and xmax)
        {
            if (P[i] is above or on L_min)
                 Ignore it and continue.
            while (there are at least 2 points on the stack)
            {
                 Let PT1 = the top point on the stack.
                 Let PT2 = the second point on the stack.
                 if (P[i] is strictly left of the line from PT2 to PT1)
                     break out of this while loop.
                 Pop the top point PT1 off the stack.
            }
            Push P[i] onto the stack.
        }
    (4) Push P[maxmin] onto the stack.

    Similarly, compute the upper hull stack.

    Let OMEGA = the join of the lower and upper hulls.

    Output: OMEGA = the convex hull ofS.
 

 


Implementation

Here is a "C++" implementation of the Chain Hull algorithm.

// Copyright 2001 softSurfer, 2012 Dan Sunday
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// SoftSurfer makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.
 

// Assume that a class is already given for the object:
//    Point with coordinates {float x, y;}
//===================================================================
 

//isLeft(): tests if a point is Left|On|Right of an infinite line.
//    Input:  three points P0, P1, and P2
//    Return: >0 for P2 left of the line through P0 and P1
//            =0 for P2 on the line
//            <0 for P2 right of the line
//    See: Algorithm 1 on Area of Triangles
inline float
isLeft( Point P0, Point P1, Point P2 )
{
    return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y);
}
//===================================================================


// chainHull_2D(): Andrew's monotone chain 2D convex hull algorithm
//     Input:  P[] = an array of 2D points
//                  presorted by increasing x and y-coordinates
//             n =  the number of points in P[]
//     Output: H[] = an array of the convex hull vertices (max is n)
//     Return: the number of points in H[]
int
chainHull_2D( Point* P, int n, Point* H )
{
    // the output array H[] will be used as the stack
    int    bot=0, top=(-1);   // indices for bottom and top of the stack
    int    i;                 // array scan index

    // Get the indices of points with min x-coord and min|max y-coord
    int minmin = 0, minmax;
    float xmin = P[0].x;
    for (i=1; i<n; i++)
        if (P[i].x != xmin) break;
    minmax = i-1;
    if (minmax == n-1) {       // degenerate case: all x-coords == xmin
        H[++top] = P[minmin];
        if (P[minmax].y != P[minmin].y) // a  nontrivial segment
            H[++top] =  P[minmax];
        H[++top] = P[minmin];            // add polygon endpoint
        return top+1;
    }

    // Get the indices of points with max x-coord and min|max y-coord
    int maxmin, maxmax = n-1;
    float xmax = P[n-1].x;
    for (i=n-2; i>=0; i--)
        if (P[i].x != xmax) break;
    maxmin = i+1;

    // Compute the lower hull on the stack H
    H[++top] = P[minmin];      // push  minmin point onto stack
    i = minmax;
    while (++i <= maxmin)
    {
        // the lower line joins P[minmin]  with P[maxmin]
        if (isLeft( P[minmin], P[maxmin], P[i])  >= 0 && i < maxmin)
            continue;           // ignore P[i] above or on the lower line

        while (top > 0)         // there are at least 2 points on the stack
        {
            // test if  P[i] is left of the line at the stack top
            if (isLeft(  H[top-1], H[top], P[i]) > 0)
                 break;         // P[i] is a new hull  vertex
            else
                 top--;         // pop top point off  stack
        }
        H[++top] = P[i];        // push P[i] onto stack
    }

    // Next, compute the upper hull on the stack H above  the bottom hull
    if (maxmax != maxmin)      // if  distinct xmax points
         H[++top] = P[maxmax];  // push maxmax point onto stack
    bot = top;                  // the bottom point of the upper hull stack
    i = maxmin;
    while (--i >= minmax)
    {
        // the upper line joins P[maxmax]  with P[minmax]
        if (isLeft( P[maxmax], P[minmax], P[i])  >= 0 && i > minmax)
            continue;           // ignore P[i] below or on the upper line

        while (top > bot)     // at least 2 points on the upper stack
        {
            // test if  P[i] is left of the line at the stack top
            if (isLeft(  H[top-1], H[top], P[i]) > 0)
                 break;         // P[i] is a new hull  vertex
            else
                 top--;         // pop top point off  stack
        }
        H[++top] = P[i];        // push P[i] onto stack
    }
    if (minmax != minmin)
        H[++top] = P[minmin];  // push  joining endpoint onto stack

    return top+1;
}
 


References

S.G. Akl & Godfried Toussaint,  "Efficient Convex Hull Algorithms for Pattern Recognition Applications", Proc.  4th Int'l Joint Conf. on Pattern Recognition, Kyoto, Japan, 483-487 (1978)

A.M. Andrew, "Another Efficient  Algorithm for Convex Hulls in Two Dimensions", Info.  Proc. Letters 9, 216-219 (1979)

A. Bykat, "Convex Hull of a Finite  Set of Points in Two Dimensions", Info.  Proc. Letters 7, 296-298 (1978)

W. Eddy, "A New Convex Hull Algorithm  for Planar Sets", ACM Trans. Math. Software 3(4), 398-403 (1977)

Ronald Graham, "An Efficient  Algorithm for Determining the Convex Hull of a Finite Point Set", Info.  Proc. Letters 1, 132-133 (1972)

R.A. Jarvis, "On the Identification  of the Convex Hull of of a Finite Set of Points in the Plane", Info.  Proc. Letters 2, 18-21 (1973)

M. Kallay, "The Complexity of  Incremental Convex Hull Algorithms in Rd", Info.  Proc. Letters 19, 197 (1984)

D.G. Kirkpatrick & R. Seidel, "The  Ultimate Planar Convex Hull Algorithm?", SIAM Jour. Comput. 15, 287-299 (1986)

Joseph O'Rourke, Computational Geometry in C (2nd Edition), Chap. 3 "Convex Hulls in 2D"  (1998)

Franco Preparata & Michael Shamos, Computational Geometry: An Introduction,  Chap. 3 "Convex Hulls: Basic Algorithms" (1985)

Franco Preparata & S.J. Hong, "Convex  Hulls of Finite Sets of Points in Two and Three Dimensions", Comm. ACM 20, 87-93  (1977)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值