Алгоритм "разделяй и властвуй" для подсчета доминирующих точек?

предположим, что точка в координате (x1, y1) возвышается другая точка (x2,y2), если x1 ≤ x2 и y1 ≤ y2;

У меня есть набор точек (x1, y1),....(xn, yn) и я хочу найти общее количество доминирующих пар. Я могу сделать это, используя грубую силу, сравнивая все точки друг с другом, но для этого требуется время O (n2). Вместо этого я хотел бы использовать подход "разделяй и властвуй", чтобы решить это во времени O(n log n).

прямо сейчас у меня есть следующий алгоритм:

  • нарисуйте вертикальную линию, разделяющую множество точек точек на два равных подмножества Рлевый и Pправо. В качестве базового случая, если осталось всего две точки, я могу сравнить их напрямую.

  • рекурсивно подсчитайте количество доминирующих пар в Pлевый и Pправо

  • некоторые покорять шаг?

проблема в том, что я не вижу, какой шаг "завоевать" должен быть здесь. Я хочу подсчитать, сколько доминирующих пар есть, что крест от Pлевый в Pправо, но я не знаю, как это сделать, не сравнивая все точки в обеих частях, что займет время O (n2).

может кто-нибудь дать мне подсказку о том, как сделать завоевание шаг? this is my example

Итак, 2 половины координат y являются : {1,3,4,5,5} и {5,8,9,10,12}

Я провожу линию деления.

3 ответов


Предположим, вы сортируете точки в обеих половинах отдельно в порядке возрастания по их координатам Y. Теперь посмотрите на самую низкую y-значную точку в обеих половинах. Если самая низкая точка слева имеет меньшее значение y, чем самая низкая точка справа, то в этой точке доминируют все точки справа. В противном случае нижняя точка справа не доминирует ни над чем слева.

в любом случае вы можете удалить одну точку из одной из двух половин и повторить процесс с оставшимися отсортированными списками. Это O(1) работает на точку, поэтому, если есть N общих точек, Это O (n) работает (после сортировки), чтобы подсчитать количество доминирующих пар через две половины. Если вы видели это раньше, это похоже на алгоритм подсчета инверсий в массиве).

Факторинг времени, необходимого для сортировки точек (O(N log n)), этот шаг завоевания занимает O (n log n) времени, что дает повторение

T(n) = 2T(n / 2) + O (N log n)

Это решает до O (N log2 n) согласно Главная Теорема.

тем не менее, вы можете ускорить это. Предположим, что перед тем, как начать разделять и покорять, вы заранее задаете точки по их координатам y, выполняя один проход O(N log n). Используя трюки, похожие на ближайшую проблему с парой точек, вы можете получить точки в каждой половине, отсортированной в O (n) раз по каждой подзадаче размера n (см. обсуждение внизу этой страницы) для деталей). Это изменяет повторение на

T(n) = 2T(n / 2) + O (n)

который решает O (N log n), как требуется.

надеюсь, что это помогает!


Ну, таким образом, у вас есть O(n^2) только для деления на подмножества...
Мой подход был бы другим

  1. сортировать точки по X ... O (n.log (n))
  2. теперь проверьте Y
    • но проверьте только точки с большим X (если вы сортируете их по возрастанию, то с большим индексом)
  3. Итак, теперь у вас есть O (n.log(n)+(n.n / 2))

вы также можете ускорить процесс, выполнив отдельный тест X и Y и после этого объедините результат, который приведет O (n + 3.n.log (n))

  1. добавить атрибут индекса к вашим точкам
    • где index = 0xyyyyyxxxxh-целочисленный тип без знака
    • гггг-индекс точки в y-сортированном массиве
    • XXXX-индекс точки в X-сортированном массиве
    • если у вас более 2^16 точек, используйте больше, чем 32-битный тип данных.
  2. сортировать точки по возрастанию X и установить XXXX часть их индекса O1 (n.log (n))
  3. сортировать точки по возрастанию Y и установить гггг часть их индекса O2 (n.log (n))
  4. сортировка точек по возрастанию индекса O3(n.log (n))
  5. теперь точка i доминирует над любой точкой J, Если (i
  6. но если вам нужно создать фактически все пары для любой точки
  7. это займет O4 (n.n / 2) таким образом, этот подход сэкономит не мало времени
  8. если вам нужна только одна пара для любой точки, то простой цикл хватит O4 (n-1)
  9. Итак, в этом случае O (n-1+3.n.log (n)) - > ~O(n+3.n.log (n))

надеюсь, что это помогло,... конечно, если вы застряли с этим подходом подразделения, чем у меня нет лучшего решения для вас.

PS. для этого вам не нужна дополнительная рекурсия, просто 3x сортировка и только один uint для любой точки, поэтому требования к памяти не так велики и даже должны быть быстрее, чем рекурсивный вызов рекурсии подразделения в целом


этот алгоритм работает в O(N*log (N)) где N-размер списка точек и он использует O (1) дополнительное пространство.

выполните следующие действия:

  1. сортировка списка точек по координате y (по возрастанию), разрыв связей по X-координата (в порядке возрастания).
  2. перейдите по отсортированному списку в обратном порядке, чтобы подсчитать доминирующие точки: если текущее значение X-координаты >= max X-координаты встречается так далеко затем увеличьте результат и обновите max.

это работает, так как вы точно знаете, что если все пары с большими координатами y имеют меньшую координату x, чем текущая точка, вы нашли доминирующие точки. Этап сортировки делает его действительно эффективным.

вот код Python:

def my_cmp(p1, p2):
    delta_y = p1[1] - p2[1]
    if delta_y != 0:
        return delta_y
    return p1[0] - p2[0]

def count_dom_points(points):
    points.sort(cmp = my_cmp)
    maxi = float('-inf')
    count = 0
    for x, y in reversed(points):
        if x >= maxi:
        count += 1
        maxi = x

    return count