【问题标题】:Convex hull algorithm that generates points in merry go round order?以旋转木马顺序生成点的凸包算法?
【发布时间】:2016-07-14 13:23:52
【问题描述】:

我采用了安德森的单调链算法来寻找凸包,但在这样做之后我发现结果点是 x 顺序的,而不是旋转木马顺序。是否有一个凸包算法以旋转木马的顺序生成点,即围绕船体周边的顺序?

这是我的单调链实现,不能满足我的问题:

// monotone chain
public static ComparablePoint[] convex_hull( ComparablePoint[] points ){
    if( points.length > 1 ){
        int ctPoints = points.length;
        int k = 0;
        ComparablePoint[] hull = new ComparablePoint[ 2 * ctPoints ];
        java.util.Arrays.sort( points );

        // Build lower hull
        for (int i = 0; i < ctPoints; ++i) {
            while (k >= 2 && crossProduct(hull[k - 2], hull[k - 1], points[i]) <= 0)
                k--;
            hull[k++] = points[i];
        }

        // Build upper hull
        for (int i = ctPoints - 2, t = k + 1; i >= 0; i--) {
            while (k >= t && crossProduct(hull[k - 2], hull[k - 1], points[i]) <= 0)
                k--;
            hull[k++] = points[i];
        }
        if (k > 1) {
            hull = java.util.Arrays.copyOfRange(hull, 0, k - 1); // remove non-hull vertices after k; remove k - 1 which is a duplicate
        }
        return hull;
    } else if( points.length <= 1 ){
        return points;
    } else{
        return null;
    }
}

为了清楚我所说的旋转木马顺序是什么意思:凸包上的点位于一个凸多边形的周长中。当您绕过多边形的周边时,我需要将这些点按顺序排列。

上面显示的单调链算法不这样做,它按 x 坐标的顺序返回点。首先是 x 坐标最低的点,然后是 x 坐标第二低的点,以此类推。

【问题讨论】:

  • 如何在没有一些既定的“轴”的情况下定义旋转木马顺序?
  • 普通的格雷厄姆扫描可以做到这一点。它以 X 坐标顺序给出船体的“底部”和“顶部”部分,因此只需反转一个或另一个并连接。
  • 如果有兴趣,这里有一个java实现:sourceforge.net/p/wpbdc/wpbd/ci/master/tree/src/bridgedesigner/…
  • @user2864740 凸多边形的美妙之处在于任何内部点都可以。我想 OP 只是说顺时针或逆时针(多边形)顺序。
  • 有没有人尝试运行他的代码?!它以旋转木马的顺序给船体。 -__-

标签: algorithm convex-hull


【解决方案1】:

以下算法按照您的描述对船体上的点进行排序。它类似于@AyushMishra 提供的答案,但还解决了两个点具有相同 X(或 Y)值的情况。

/**
 * Sorts the given array according to "merry-go-round" order. The array is
 * sorted in-place. The ordering is clockwise ending with the bottom-most
 * point.
 * 
 * @param points
 *            An array of points on a convex hull.
 */
public static void sortPoints(Point[] points) {

    // Ensure the input array is sorted by x-value
    Arrays.sort(points, (o1, o2) -> Double.compare(o1.getX(), o2.getX()));

    // get the index of the point with the smallest Y-value
    int bottomMost = 0;
    for (int i = 0; i < points.length; i++) {
        if (points[i].getY() < points[bottomMost].getY())
            bottomMost = i;
    }

    final Comparator<Point> hullComp = new Comparator<Point>() {

        @Override
        public int compare(Point o1, Point o2) {
            // handle case when Y's are the same.
            if (o1.getY() == o2.getY())
                return Double.compare(o1.getX(), o2.getX());

            // otherwise, just compare Y values
            return Double.compare(o1.getY(), o2.getY());
        }
    };

    // Sort the left side of the hull from smallest Y to largest Y
    Arrays.sort(points, 0, bottomMost, hullComp);

    // Sort the right side of the hull from largest Y to smallest Y
    Arrays.sort(points, bottomMost, points.length,
            (o1, o2) -> hullComp.compare(o2, o1));

}

我将此算法应用于this question 中的二维船体。这是结果图。 (注意:我偏移了点,这样轴就不会弄乱图片)跟踪线显示了执行中不同点的顺序:

或者,您可以使用一种算法来生成按(逆)顺时针顺序自动排序的船体。例如,Gift wrapping algorithmO(nh) 时间内以旋转木马顺序生成点,其中 h 是船体上的顶点数。该算法的伪代码(借自维基百科)是:

jarvis(S)
   pointOnHull = leftmost point in S
   i = 0
   repeat
      P[i] = pointOnHull
      endpoint = S[0]         // initial endpoint for a candidate edge on the hull
      for j from 1 to |S|
         if (endpoint == pointOnHull) or (S[j] is on left of line from P[i] to endpoint)
            endpoint = S[j]   // found greater left turn, update endpoint
      i = i+1
      pointOnHull = endpoint
   until endpoint == P[0]      // wrapped around to first hull point

【讨论】:

    【解决方案2】:

    只需将以下算法添加到您的算法中,以增加 X 顺序输出点。

    我们将根据您的算法输出生成凸包的上半部分和下半部分。

    让我们在凸包上取极值点。将它们命名为 L 和 R。[L 是 X 坐标最小的点,R 是 X 坐标最大的点]。

    现在对于所有其他点,我们将检查该点是位于上半部分还是下半部分。这可以通过检查某个点 K 是否位于连接 L 和 R 的线上方或位于连接 L 和 R 的线下方来轻松完成。

    因此,我们可以将所有点分类为 lower_half 或 upper_half。

    最后的答案是:点 L [左极端,即最小 X] + 上部分中的点,按 X 递增顺序,点 R[右极端,即最大 X] + 下部分中的点,按 X 递减顺序。

    注意:以上算法的复杂度是O(n),所以不会影响你算法的运行时间复杂度,加上后你的解法复杂度还是O(n log n)。

    【讨论】:

    • 这是解决问题的合理方法。
    • 如果这回答了问题,你能接受这个解决方案吗?
    猜你喜欢
    • 2021-01-27
    • 2022-08-03
    • 2014-02-01
    • 1970-01-01
    • 2012-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多