На собеседовании получил задачку:
Есть два прямоугольника. У каждого известны координаты левый верх и
правый низ. Нужно написать функцию, которая ответила бы на вопрос о
том, пересекаются ли эти прямоугольники.
Задачку я решил с помощью массивов и собеседование давно закончилось, но подозреваю, что решение не достаточно хорошее:
def gen_rectangle(cords):
"""Генерирует массив координат всех занимаемых прямоугольником точек"""
rectangle = []
for i in range(cords[0][0], cords[1][0]):
for j in range(cords[0][1], cords[1][1]):
rectangle.append((i, j))
return rectangle
def intersection(cord1, cord2):
"""Есть ли пересечение"""
rectangle1 = gen_rectangle(cord1)
rectangle2 = gen_rectangle(cord2)
for i in rectangle1:
for j in rectangle2:
if i == j:
return 'We have intersection!'
return 'All good'
cord1 = ((1, 1), (4, 8)) # первый прямоугольник
cord2 = ((3, 3), (16, 20)) # второй прямоугольник
print(intersection(cord1, cord2))
Например: если прямоугольники будут ну очень большими, то каждая занимаемая одним прямоугольником точка — это два int, такое решение не оптимально расходует память.
Как мое решение можно улучшить?
Содержание
- 1 Определение
- 2 Как строится?
- 3 Как отвечать на запрос?
- 4 Псевдокод
- 5 Причем тут пересечение прямоугольника с множеством прямоугольников?
Определение
Priority Search Tree позволяет решать следующую задачу. Даны точек, а также запросы. Каждый запрос характеризуется отрезком по одной координате , а также числом по другой координате. Для каждого запроса структура возвращает все точки, которые находятся в отрезке по одной координате, и имеют другую координату не больше . На каждый запрос дерево отвечает за , где — количество точек в ответе.
Как строится?
Дерево строится рекурсивно. Выберем точку с наименьшей -координатой (если таких несколько, то из них выберем с наименьшим ). Все остальные точки отсортируем по и разобьем на две равные (количество может отличаться на один) части. Далее будем считать, что все точки первой части имеют координату меньше всех точек второй части (с одинаковыми поступаем как обычно — сравниваем по ). Для каждого из двух множеств, если оно не пусто, построим дерево рекурсивно.
Теорема: |
PST занимает памяти. |
Доказательство: |
Каждая точка будет корнем ровно одного дерева. Количество деревьев не превышает количества точек. В сумме . |
Теорема: |
Максимальная глубина PST . |
Доказательство: |
При рекурсивном вызове количество точек уменьшается как минимум в два раза. |
Теорема: |
Построение PST работает за времени. |
Доказательство: |
Необходимо отсортировать точки по — для этого необходимо . Построение каждого дерева требует , где — количество точек в дереве, чтобы найти минимальную по точку, а также для построения новых списков точек для сыновей. На каждом уровне точек, уровней , значит, в сумме тоже . |
Как отвечать на запрос?
Если корня больше , то никакие точки дерева не могут быть в ответе. Будем хранить также минимальную и максимальную по точки, которые лежат в поддереве. Тогда, если отрезок запроса по не пересекается с отрезком по всех точек поддерева, то также никакие точки из дерева не должны быть добавлены в ответ. Если корень дерева лежит в нужном интервале, добавим его в ответ. Вызовем рекурсивно поиск ответа для двух сыновей.
Теорема: |
Будет получен правильный ответ на запрос. |
Доказательство: |
Вроде бы очевидно. |
Теорема: |
Ответ на запрос работает за , где — размер ответа. |
Доказательство: |
Тут все сложнее. Представим, что мы ищем и (из запроса) в PST как в обычном дереве поиска по . Рассмотрим все точки, которые лежат между двумя путями поиска. Среди них рассмотрим точки с . Ровно эти точки и есть ответ. Разобьем все вершины на три группы. Первая — вершины, которые лежат хотя бы на одном из двух путей. Их . Вторая — вершины, которые находятся между двумя путями. Третья — все остальные. Время обработки вершин первой группы не превышает . Как только мы зашли в вершину третей группы, сразу поймем, что отрезок по не пересекается с запросом и выйдем. А зайти в них мы можем только из вершин, которые лежат на двух путях (а их ). Отлично! Зайдя в вершину второй группы, мы либо ее добавим в ответ и продолжим, либо выйдем, так как единственная причина, почему корень не подошел — его слишком большой, а, значит, и все вершины поддерева не подходят. В итоге их обработка занимает . В сумме все хорошо! |
Псевдокод
struct PST point root point min_y, max_y PST *down, *up
// pts sorted by y PST * build_PST(vector<point> pts) if (pts.size() == 0) return NULL min_y = max_ y = root = min_element(pts) // by x remove_element(pts, root) int mid = pts.size() / 2 vector<point> down_pts = pts[0..mid] vector<point> up_pts = pts[mid+1..pts.size() - 1] down = build_PST(down_pts) up = build_PST(up_pts) if (down != NULL) min_y = min(min_y, down->min_y) if (up != NULL) max_y = max(max_y, up->max_y)
void req(PST * tree, int y1, int y2, int x_max, vector<point> & ans) if (tree == NULL) return if (x_max < tree->root.x) return if (y1 > tree->max_y || y2 < tree->min_y) return if (tree->root in [y1..y2]x[-inf;x_max]) answer.push_back(tree->root) req(tree->down, y1, y2, x_max, ans) req(tree->up, y1, y2, x_max, ans)
Причем тут пересечение прямоугольника с множеством прямоугольников?
Задача решается следующим образом. Будем находить ответ тремя разными способами, а их объединение и будет настоящим ответом. Будем считать, что объединение множеств можно делать за , где — суммарное количество элементов в них.
Первый способ. Найдем все прямоугольники, в которых прямоугольник из запроса лежит полностью. Для этого нужно решать такую задачу. Дано множество прямоугольников, для заданной точки найти все прямоугольники, в которых она лежит. Это точно можно решать за с помощью двухмерного дерева интервалов. А может можно и проще.
Второй способ. Найдем все прямоугольники, которые полностью лежат внутри заданного в запросе. Для этого воспользуемся range tree. Просто для каждого прямоугольника добавим в range tree его левый верхний угол. Задав запрос в виде нашего прямоугольника, мы получим то, что и нужно.
Третий способ. Найдем все прямоугольники, хотя бы одна сторона которого пересекает заданный в запросе. Для этого воспользуемся деревом отрезков. В структуру добавим все стороны всех прямоугольников и зададим запрос в виде нашего прямоугольника.
Утверждается, что все прямоугольники, которые нам нужны — объединение ответов, полученных тремя способами. Оценивать асимтотику всего этого мне совсем не хочется, но, наверное, это времени на запрос. памяти из-за range tree и на построение. Но, возможно, я что-то забыл или что-то можно сделать более эффективно.
Сообщения без ответов | Активные темы | Избранное
Правила форума
В этом разделе нельзя создавать новые темы.
|
Пересечения прямоугольников 09.07.2014, 01:30 |
21/09/13 |
Подскажите как доказать, что если любые три прямоугольника из некоторого множества пересекаются, то существует точка, которая принадлежит всем им.
|
|
|
ET |
Re: Пересечения прямоугольников 09.07.2014, 04:26 |
08/05/08 |
Теорема Хелли? множества «выпуклых множеств»
|
|
|
Cash |
Re: Пересечения прямоугольников 09.07.2014, 10:36 |
||
12/09/10 |
Для прямоугольников верно и в случае бесконечного их количества.
|
||
|
|||
Skeptic |
Re: Пересечения прямоугольников 09.07.2014, 14:25 |
01/12/11 |
Это автоматически следует из понятия «пересечение».
|
|
|
Aritaborian |
Re: Пересечения прямоугольников 09.07.2014, 17:10 |
11/06/12 |
Нет, не следует. Иначе зачем Хелли?
|
|
|
TopLalka |
Re: Пересечения прямоугольников 10.07.2014, 14:42 |
21/09/13 |
Я нашел доказательства для двумерного случая и конечного количества выпуклых фигур. Подскажите как пользуясь компактностью доказать и для бесконечного случая.
|
|
|
Someone |
Re: Пересечения прямоугольников 10.07.2014, 14:45 |
||
23/07/05 |
Подскажите как пользуясь компактностью доказать и для бесконечного случая. Если пересечение любого конечного набора компактов из данного семейства не пусто, то…
|
||
|
|||
TopLalka |
Re: Пересечения прямоугольников 10.07.2014, 16:57 |
21/09/13 |
|
|
|
Skeptic |
Re: Пересечения прямоугольников 11.07.2014, 07:28 |
01/12/11 |
Возьмём треугольник. Построим на каждой стороне треугольника с внешней стороны по прямоугольнику. Эти прямоугольники пересекаются, но не имеют общих точек, принадлежащих одновременно всем прямоугольникам.
|
|
|
TOTAL |
Re: Пересечения прямоугольников 11.07.2014, 07:45 |
||
23/08/07 |
Возьмём треугольник. Построим на каждой стороне треугольника с внешней стороны по прямоугольнику. Эти прямоугольники пересекаются, но не имеют общих точек, принадлежащих одновременно всем прямоугольникам. Я тоже так могу:
|
||
|
|||
Cash |
Re: Пересечения прямоугольников 11.07.2014, 08:41 |
||
12/09/10 |
|||
|
|||
Skeptic |
Re: Пересечения прямоугольников 11.07.2014, 08:43 |
01/12/11 |
TOTAL , соотнесите это с постановкой задачи и попробуйте её доказать.
|
|
|
TOTAL |
Re: Пересечения прямоугольников 11.07.2014, 08:49 |
||
23/08/07 |
Прямоугольник задается двумя вершинами и Две вершины мало, ведь прямоугольник можно наклонять.
|
||
|
|||
Cash |
Re: Пересечения прямоугольников 11.07.2014, 08:51 |
||
12/09/10 |
ошибиться вроде негде… Две вершины мало, ведь прямоугольник можно наклонять. Ну вот оно и есть, сознание замутненное пикселями
|
||
|
|||
Skeptic |
Re: Пересечения прямоугольников 11.07.2014, 11:19 |
01/12/11 |
Задача решается при дополнительном условии: стороны прямоугольников параллельны осям координат.
|
|
|
Модераторы: Модераторы Математики, Супермодераторы
Кто сейчас на конференции |
Сейчас этот форум просматривают: нет зарегистрированных пользователей |
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения |
I implemented it like this:
bool rectCollision(const CGRect &boundsA, const Matrix3x3 &mB, const CGRect &boundsB)
{
float Axmin = boundsA.origin.x;
float Axmax = Axmin + boundsA.size.width;
float Aymin = boundsA.origin.y;
float Aymax = Aymin + boundsA.size.height;
float Bxmin = boundsB.origin.x;
float Bxmax = Bxmin + boundsB.size.width;
float Bymin = boundsB.origin.y;
float Bymax = Bymin + boundsB.size.height;
// find location of B corners in A space
float B0x = mB(0,0) * Bxmin + mB(0,1) * Bymin + mB(0,2);
float B0y = mB(1,0) * Bxmin + mB(1,1) * Bymin + mB(1,2);
float B1x = mB(0,0) * Bxmax + mB(0,1) * Bymin + mB(0,2);
float B1y = mB(1,0) * Bxmax + mB(1,1) * Bymin + mB(1,2);
float B2x = mB(0,0) * Bxmin + mB(0,1) * Bymax + mB(0,2);
float B2y = mB(1,0) * Bxmin + mB(1,1) * Bymax + mB(1,2);
float B3x = mB(0,0) * Bxmax + mB(0,1) * Bymax + mB(0,2);
float B3y = mB(1,0) * Bxmax + mB(1,1) * Bymax + mB(1,2);
if(B0x<Axmin && B1x<Axmin && B2x<Axmin && B3x<Axmin)
return false;
if(B0x>Axmax && B1x>Axmax && B2x>Axmax && B3x>Axmax)
return false;
if(B0y<Aymin && B1y<Aymin && B2y<Aymin && B3y<Aymin)
return false;
if(B0y>Aymax && B1y>Aymax && B2y>Aymax && B3y>Aymax)
return false;
float det = mB(0,0)*mB(1,1) - mB(0,1)*mB(1,0);
float dx = mB(1,2)*mB(0,1) - mB(0,2)*mB(1,1);
float dy = mB(0,2)*mB(1,0) - mB(1,2)*mB(0,0);
// find location of A corners in B space
float A0x = (mB(1,1) * Axmin - mB(0,1) * Aymin + dx)/det;
float A0y = (-mB(1,0) * Axmin + mB(0,0) * Aymin + dy)/det;
float A1x = (mB(1,1) * Axmax - mB(0,1) * Aymin + dx)/det;
float A1y = (-mB(1,0) * Axmax + mB(0,0) * Aymin + dy)/det;
float A2x = (mB(1,1) * Axmin - mB(0,1) * Aymax + dx)/det;
float A2y = (-mB(1,0) * Axmin + mB(0,0) * Aymax + dy)/det;
float A3x = (mB(1,1) * Axmax - mB(0,1) * Aymax + dx)/det;
float A3y = (-mB(1,0) * Axmax + mB(0,0) * Aymax + dy)/det;
if(A0x<Bxmin && A1x<Bxmin && A2x<Bxmin && A3x<Bxmin)
return false;
if(A0x>Bxmax && A1x>Bxmax && A2x>Bxmax && A3x>Bxmax)
return false;
if(A0y<Bymin && A1y<Bymin && A2y<Bymin && A3y<Bymin)
return false;
if(A0y>Bymax && A1y>Bymax && A2y>Bymax && A3y>Bymax)
return false;
return true;
}
The matrix mB is any affine transform matrix that converts points in the B space to points in the A space. This includes simple rotation and translation, rotation plus scaling, and full affine warps, but not perspective warps.
It may not be as optimal as possible. Speed was not a huge concern. However it seems to work ok for me.
Калькулятор выполняет разбиение пересекающим прямоугольником пересекаемого. В результате разбиения может получиться от одного до четырех новых прямоугольников. В качестве результата выводятся данные таких прямоугольников в виде четырех чисел: координат левого нижнего угла прямоугольника x и y, ширины и высоты.
Обратите внимание, что получающиеся в результате прямоугольники могут накладываться или пересекаться друг с другом. То есть они НЕ являются попарно непересекающимися. Варианты таких прямоугольников приведены на картинке ниже:
Это сделано специально — таким образом решается задача получения максимально возможных размеров новых прямоугольников, что полезно для некоторых алгоритмов, например, для алгоритма максимальных прямоугольников (Maximal Rectangles Algorithm1) используемого для решения задачи двумерной упаковки в контейнеры.
Пересечение прямоугольников
Пересекаемый прямоугольник
Пересекающий прямоугольник
Точность вычисления
Знаков после запятой: 2