Алгоритм, чтобы взять объединение прямоугольников и посмотреть, является ли объединение еще прямоугольником - PullRequest
9 голосов
/ 01 февраля 2012

У меня есть проблема, в которой я должен проверить, образует ли объединение данного набора прямоугольников прямоугольник или нет.У меня нет большого опыта решения задач вычислительной геометрии.Мой подход к проблеме заключался в том, что, поскольку я знаю координаты всех прямоугольников, я могу легко отсортировать точки и затем определить угловые точки наибольшего возможного прямоугольника.Тогда я мог бы провести линию и посмотреть, все ли точки на линии попадают внутрь прямоугольника.Но этот подход ошибочен, и он потерпит неудачу, потому что объединение может быть в форме буквы «U».Я бы очень помог, если бы вы толкнули меня в правильном направлении.

Ответы [ 10 ]

5 голосов
/ 01 февраля 2012

Ваша собственная версия не учитывает, что края прямоугольников могут быть не параллельны друг другу. Следовательно, не может быть «возможно большего прямоугольника».

Я бы попробовал этот общий подход:

1) Найдите выпуклый корпус. Вы можете найти алгоритмы расчета выпуклой оболочки здесь http://en.wikipedia.org/wiki/Convex_hull_algorithms.

2) Проверьте, является ли выпуклая оболочка прямоугольником. Вы можете сделать это, пройдя по всем точкам выпуклого корпуса и проверив, все ли они образуют углы 180 или 90 градусов. Если это не так, объединение не является прямоугольником.

3) Пройдите все точки на выпуклой оболочке. Для каждой точки проверьте, находится ли средняя точка между ThisPoint и NextPoint на краю любого первоначально данного прямоугольника. Если каждая средняя точка имеет, объединение является прямоугольником. Если это не так, объединение не является прямоугольником.

Сложность будет O (n log h) для нахождения выпуклой оболочки, O (h) для второй части и O (h * n) для третьей части, где h - количество точек на выпуклой оболочке.

Edit: Если цель состоит в том, чтобы проверить, является ли полученный объект заполненным прямоугольником , а не только прямоугольником с краями и углами , тогда добавьте шаг (4).

4) Найдите все отрезки, которые образованы пересекающимися или касающимися прямоугольниками. Обратите внимание - по определению все эти отрезки являются отрезками ребер данных прямоугольников. Если прямоугольник не касается и не пересекает другие прямоугольники, отрезки линии являются его краями.

Для каждого отрезка линии проверьте, равна ли его средняя точка

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

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

1 голос
/ 01 февраля 2012

Просто на ум пришел простой подход: если два прямоугольника разделяют ребро [1], то вместе они образуют прямоугольник, который содержит оба - либо смежные прямоугольники [][ ], либо один содержит другой [[] ].

Таким образом, если список прямоугольников образует прямоугольник большего размера, то все, что вам нужно, это многократно перебирать прямоугольники и «объединять» их пары в один более крупный. Если за одну итерацию вы не можете объединить ни одного, то с этими частями невозможно создать прямоугольник большего размера, чем у вас уже есть; в противном случае вы будете продолжать объединять прямоугольники до тех пор, пока не останется один.

[1] Поделись, так как у них одинаковый край; для одного из них недостаточно иметь ребро, включенное в ребро другого.

эффективность

Поскольку эффективность, по-видимому, является проблемой, вы, вероятно, могли бы ускорить ее, создав два индекса прямоугольников, один с большим размером ребра, а другой с меньшим размером края.

Затем сравните ребра одинакового размера и, если они совпадают, объедините два прямоугольника, удалите их из индексов и добавьте новый прямоугольник в индексы.

Вероятно, вы можете ускорить его, не переходя к следующей итерации при объединении чего-либо, а переходя к концу индексов перед повторением. (Останавливается, когда одна итерация не объединяет или остался только один прямоугольник.)

Кроме того, края прямоугольника, полученные в результате объединения, всегда по результатам анализа равны или превышают края исходных прямоугольников.
Таким образом, если индексы упорядочены по возрастанию размера ребра, новый прямоугольник будет вставлен либо в ту же позицию, что и проверяемая, либо в позиции, которые еще предстоит проверить, поэтому каждое объединение не потребует дополнительного цикла итерации. (Поскольку новый прямоугольник, несомненно, не объединится ни с одним прямоугольником, ранее проверенным в этой итерации, поскольку его ребра больше, чем все отмеченные ребра.)
Для этого на каждом шаге определенной итерации вам нужно попытаться объединиться по следующему меньшему ребру из любого из индексов:

  • Если вы находитесь в index1 = 3 и index2 = 6, вы проверяете index1 и повышаете этот индекс;
  • Если следующее ребро в этом индексе равно 5, следующий шаг итерации будет в index1 = 5 и index2 = 6, поэтому он проверит index1 и улучшит этот индекс;
  • Если следующее ребро в этом индексе равно 7, следующий шаг итерации будет в index1 = 7 и index2 = 6, поэтому он проверит index2 и улучшит этот индекс;
  • Если следующее ребро в этом индексе равно 10, следующий шаг итерации будет в index1 = 7 и index2 = 10, поэтому он проверит index1 и улучшит этот индекс;
  • и т.д.

примеры

[A  ][B   ]
[C ][D    ]

A можно объединить с B, C с D, а затем AB с CD. Один слева, ABCD, таким образом, возможно.

[A  ][B   ]

[C ][D     ]

A можно объединить с B, C с D, но AB нельзя объединить с CD. 2 слева, AB и CD, таким образом, невозможно.

[A  ][B    ]
[C ][D  [E]]

A можно объединить с B, C с D, CD с E, CDE с AB. 1 слева, ABCDE, таким образом, возможно.

[A  ][B    ]
[C ][D     ][E]

A можно объединить с B, C с D, CD с AB, но не E. 2 слева, ABCD и E, таким образом, это невозможно.

ловушка

Если прямоугольник содержится в другом, но не разделяет границу, такой подход не объединит их.

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

Это все еще не касается двух ситуаций.

Сначала рассмотрим ситуацию, когда с этой картой:

A  B  C  D
E  F  G  H

у нас есть прямоугольники ACGE и BDFH. Эти прямоугольники не имеют общего края и не содержатся, но образуют прямоугольник большего размера.

Во-вторых, рассмотрим ситуацию, когда с этой картой:

A B C D
E F G H
I J K L

у нас есть прямоугольники ABIJ, CDHG и EHLI. Они не разделяют ребра, не содержатся внутри друг друга, и никакие два из них не могут быть объединены в один прямоугольник; но в целом прямоугольник.


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

1 голос
/ 01 февраля 2012

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

Вы не можете просто смотреть на проекцию каждого прямоугольника на края ограничивающего прямоугольника, если только проблема не позволяет оставить отверстия в середине прямоугольника, хотя это, вероятно, быстрая начальная проверка, которая может быть выполнена перед следующим исчерпывающим подходом:

  1. Запуск по списку один раз, вычисление минимальных и максимальных координат x и y и площади каждого прямоугольника
  2. Создайте список ввода, содержащий ваши прямоугольники ввода, отсортированные по убыванию.
  3. Первоначально создать рабочий список, содержащий ограничивающий прямоугольник
  4. Пока в рабочем списке есть прямоугольники
    1. Возьмите самый большой прямоугольник в списке ввода R
    2. Создать пустой список для фрагментов
    3. для каждого прямоугольника r в рабочем списке, пересекайте r с R, разбивая r на прямоугольную часть, содержащуюся в R (если есть), и ноль или более прямоугольников, не входящих в R. Если r был разбит, отбросьте часть, содержащуюся в R и добавьте оставшиеся прямоугольники в список фрагментов.
    4. добавить содержимое списка фрагментов в рабочий список
1 голос
/ 01 февраля 2012

Предполагая, что ваши прямоугольники выровнены по оси координат:

Учитывая два прямоугольника A, B, вы можете создать функцию, которая вычитает B из A и возвращает наборпрямоугольники A (это может быть пустой набор): Set = subtract_rectangle(A, B)

Затем, учитывая набор прямоугольников R, для которого вы хотите узнать, является ли их объединение прямоугольником:

  1. Рассчитать максимальный прямоугольник Big, который охватывает все прямоугольники, как ((min_x,min_y)-(max_x,max_y))

  2. сделать набор S содержащим прямоугольник Большой: S = (Big)

  3. для каждого прямоугольника B в R:

    1. S1 = ()

    2. для каждого прямоугольника A в S:

      1. S1 = S1 + subtract_rectangle(A, B)
    3. S = S1

    4. если S пусто, то объединение прямоугольников является прямоугольником.

  4. Конец, S содержит части Big notпокрыты любым прямоугольником от R

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

1 голос
/ 01 февраля 2012

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

Если максимально допустимый прямоугольник является допустимым, то каждая его сторона должна быть объединением сторон исходных прямоугольников. Вы можете отсканировать исходный набор прямоугольников, найти те прямоугольники, которые являются частью самой большой стороны, которую мы ищем (это можно сделать в O (n), проверив, если X == mostRectangle.Top.X, если вы смотрите сверху сторона и т. д.), назовем их S.

Для каждой стороны s в S мы можем создать интервал [от, до]. Все, что нам нужно, это проверить, соответствует ли объединение всех интервалов стороне наибольшего прямоугольника. Это можно сделать в O (nlog (n)) стандартными алгоритмами или в среднем O (n) с помощью некоторого хеш-трюка (см. http://www.careercup.com/question?id=12523672, см. Мой последний комментарий (последнего комментария) там для O (n) алгоритм).

Например, скажем, мы получили два прямоугольника 1 * 1 в первом квадранте, левые нижние координаты (0,0) и (1,0). Самый большой прямоугольник - 2 * 1 с левой нижней координатой (0,0). Поскольку [0,1] Union [1,2] равно [0,2], верхняя и нижняя стороны соответствуют наибольшему прямоугольнику, аналогичному для левой и правой стороны.

Теперь предположим, что мы получили форму U. 3 * 1 в (0,0), 1 * 1 в (0,1), 1 * 1 в (2,1), мы получили самый большой прямоугольник 3 * 2 в (0,0). Так как для верхней стороны мы получили [0,1] Union [1,3] не совпадает с [0,3], алгоритм выведет объединение вышеуказанных прямоугольников не является прямоугольником.

Таким образом, вы можете сделать это в среднем за O (n) или, по крайней мере, за O (nlog (n)), если вы не хотите связываться с каким-либо сложным алгоритмом хэширования. Гораздо лучше, чем O (n ^ 4)!

Редактировать: У нас небольшая проблема, если где-то посередине всех прямоугольников существует пустое пространство. Дайте мне подумать об этом ....

Edit2: простой способ обнаружить пустое пространство - это для каждого угла прямоугольника, который не является точкой на самом большом прямоугольнике, мы немного отклонимся наружу для всех четырех направлений (диагональ) и проверим, находимся ли мы еще в каком-либо прямоугольник. Это O (n ^ 2). (Что разрушает мою прекрасную O (nlog (n))! Может кто-нибудь может придумать лучшую идею?

1 голос
/ 01 февраля 2012

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

0 голосов
/ 09 марта 2019

Общий случай, мышление в образах:

  1. | external_rect - объединение (внутренние прямоугольники) |
  2. Проверьте, что результат равен нулю
0 голосов
/ 06 февраля 2012

Мне наконец-то удалось найти впечатляющий проект javascript (благодаря поиску в github :)!)

https://github.com/evanw/csg.js

Также посмотрите мой ответ здесь с другими интересными проектами

0 голосов
/ 01 февраля 2012

Как сказал jva: «Ваша собственная версия не учитывает, что края прямоугольников могут быть не параллельны друг другу». Этот ответ также предполагает "параллельные" прямоугольники.

Если у вас есть сетка, в отличие от необходимости бесконечной точности, в зависимости от количества и размеров прямоугольников и гранулярности сетки, может быть целесообразно использовать ее методом грубой силы.

Просто возьмите ваш "самый большой прямоугольник из возможных" и проверьте все его точки, чтобы увидеть, находится ли каждая точка хотя бы в одном из меньших прямоугольников.

0 голосов
/ 01 февраля 2012

Может быть ...

Соберите все x-координаты в списке и отсортируйте их.Из этого списка создайте последовательность смежных интервалов.Сделайте то же самое для y-координат.Теперь у вас есть два списка интервалов.Для каждой пары интервалов (A=[x1,x2] из x-списка, B=[y1,y2] из y-списка) сделайте прямоугольник их продукта A x B = (x1,y1)-(x2,y2)

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

Сделать это эффективным (я думаю, я предложил алгоритм O (n 4 ))совсем другой вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...