Алгоритм выпуклой оболочки, который генерирует точки в карусели?

Я адаптировал монотонный цепной алгоритм Андерсона для поиска выпуклой оболочки, но после этого обнаружил, что результирующие точки расположены в порядке 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 и так далее.


person Tyler Durden    schedule 27.03.2016    source источник
comment
Как определить карусельный порядок без установленного «акселя»?   -  person user2864740    schedule 27.03.2016
comment
Обычное сканирование Грэма делает это. Это дает нижнюю и верхнюю части корпуса в порядке X-координаты, поэтому просто поменяйте местами одну или другую и соедините.   -  person Gene    schedule 27.03.2016
comment
В случае интереса, вот реализация Java: sourceforge.net/p/wpbdc/wpbd/ci/master/tree/src/bridgedesigner/   -  person Gene    schedule 27.03.2016
comment
@user2864740 user2864740 Прелесть выпуклых многоугольников в том, что подойдет любая внутренняя точка. Я полагаю, что ОП просто говорит по часовой стрелке или против часовой стрелки (многоугольник).   -  person Gene    schedule 27.03.2016
comment
Кто-нибудь пытался запустить его код?! Это подача корпуса в порядке карусели. -__-   -  person Ayush Mishra    schedule 27.03.2016


Ответы (2)


Следующий алгоритм сортирует точки на корпусе, как вы описываете. Он похож на ответ, предоставленный @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));

}

Я применил этот алгоритм к 2D-корпусу, найденному в этом вопросе. Вот схема результатов. (Примечание: я сместил точки, чтобы оси не загромождали картинку) Линии трассировки показывают порядок в разных точках выполнения:

результат алгоритма сортировки

В качестве альтернативы вы можете использовать алгоритм, который создает корпус, который автоматически сортируется в порядке (против) часовой стрелки. Например, алгоритм упаковки подарков выдает баллы в порядке карусели в O(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
person Austin D    schedule 27.03.2016

Просто добавьте следующий алгоритм в свой алгоритм, который выводит точку в порядке возрастания X.

Мы сгенерируем верхнюю половину и нижнюю половину выпуклой оболочки из выходных данных вашего алгоритма.

Возьмем крайние точки на выпуклой оболочке. Назовите их L и R. [L — точка с минимальной координатой X, R — точка с максимальной координатой X].

Теперь для всех остальных точек мы проверим, лежит ли эта точка в верхней или нижней половине. Это легко сделать, проверив, лежит ли некоторая точка K выше линии, соединяющей L и R, или ниже линии, соединяющей L и R.

Таким образом, мы можем классифицировать все точки либо по нижней_половине, либо по верхней_половине.

Наконец, ответ будет таким: Точка L [крайнее слева, т.е. минимум X] + точки в верхней_части в возрастающем порядке X, точка R[крайняя справа, т.е. максимум X] + точки в нижней_части в порядке убывания X.

Примечание. Сложность приведенного выше алгоритма составляет O (n), поэтому он не повлияет на сложность времени выполнения вашего алгоритма, и сложность вашего решения все равно будет O (n log n) после его добавления.

person Ayush Mishra    schedule 27.03.2016
comment
Это разумный подход к решению проблемы. - person Tyler Durden; 28.03.2016
comment
Если это решает проблему, можете ли вы принять решение? - person Ayush Mishra; 28.03.2016