Изменения

Перейти к: навигация, поиск

Пересечение множества отрезков

75 байт убрано, 19:42, 4 сентября 2022
м
rollbackEdits.php mass rollback
== Описание алгоритма ==
В основу алгоритма положен метод [[Файл:Status_line.png|200px|thumb|right|Заметающая прямая и события]] Воспользуемся методом заметающей прямой (sweep line), которая будет расположена расположенной горизонтально и двигаться двигающейся вниз (в сторону уменьшения y-координаты). Нас будут интересовать события (events, event points) трёх типов:
* верхний конец отрезка,
* нижний конец отрезка,
* точка пересечения пары отрезков;
причем о событиях первых двух типов мы знаем заранее, а события, являющиеся пересечением отрезков, будут обнаружены и добавлены в множество необработанных событий динамически. Будем считать, что если у двух точек равные ординаты, то выше та, что лежит левее. Таким образом верхним концом горизонтального отрезка, в силу введенного порядка, является его левый конец. Это даст нам корректную обработку вырожденного случая, когда отрезок горизонтален.  [[Файл:Sweep_line_slight_rotation.png|200px|thumb|right|Приоритет событий]] Можно представить, что заметающая прямая не горизонтальна, а повернута на малый угол против часовой стрелки, поэтому не существует такой конфигурации, что на ней лежит больше одного события (мы, для удобства, считаем точку пересечения трёх или более отрезков одним событием), а события с равными ординатами обрабатываются слева направо.
Нам потребуется две структуры данных.
Во-первых, мы будем хранить очередь событий <tex>Q</tex> в виде сбалансированного бинарного дерева поиска, что позволит извлекать следующее событие и вставлять новое событие в очередь за <tex>O(\log{m})</tex>, где <tex>m</tex> {{---}} количество элементов в очереди. Дерево будет отсортировано согласно порядку, введённому выше. Причём вместе с каждым событием мы будем хранить список отрезков, верхней точкой которых он является.
Во вторых, будем хранить статус <tex>T</tex> заметающей прямой: множество отрезков, пересекающих заметающую прямую в данный момент времени, упорядоченных слева направо. От статуса нам потребуется оптимальные вставка и удаление отрезков, поэтому по-прежнему удобно воспользоваться бинарным деревом поиска.
 
{|border="0" cellpadding="5" width=30% align=center
|[[Файл: Status_structure.png|thumb|250px|center|Статус заметающей прямой]]
|[[Файл: Neighbour_segments.png|thumb|250px|center|Соседние отрезки в статусе]]
|
|}
Главная идея заключается в том, что мы будем проверять, пересекаются ли два отрезка, если они являются соседними в статусе. Это означает, что каждый отрезок мы будем проверять на пересечение с его левым и правым соседями в статусе. Далее, по ходу выполнения алгоритма, у отрезка могут измениться соседи; когда это происходит мы снова проверяем, не пересекает ли отрезок его новых соседей в статусе?
* Нижний конец отрезка. В этом случае мы удалим отрезок из статуса и проверим пару отрезков ставших соседними на пересечение. Как и в предыдущем случае, обнаруженные пересечения могут уже находиться в очереди.
{|border="0" cellpadding="5" width=30% align=center|[[Файл:Upper_and_intersection.png|thumb|250px|center|Обработка верхних концов и пересечений]]|[[Файл:Lower.png|thumb|250px|center|Обработка нижних концов]]||} Как было сказано выше, мы интерпретируем пересечение более двух отрезков в одной точке как одно событие. В этом случае обработка несколько сложнее (стоит пристально посмотреть пристально на псевдокод и рисунок {{---}} и убедиться, что алгоритм корректно обрабатывает такие события). Псевдокод функции обработки события:
handleEventPoint(p)
Q.insert(x) // должно работать корректно, если x уже есть в Q
=== Вырожденные случаи ===
Описанный алгоритм обрабатывает корректно горизонтальные отрезки и точки пересечения трёх и более отрезков, но не справляется с перекрывающимися отрезками. Об этом вскользь упомянуто здесь: {|border="0" cellpadding="5" width=30% align=center|[[Алгоритм Бентли-ОттманаФайл:Mantaining_status.png|thumb|450px|center|Поддержание статуса при обработке события]].||}
== Доказательство корректности ==
{{Лемма
|statement=Пусть <tex>p</tex> {{---}} точка пересечения нескольких отрезков, причем <tex>p</tex> не совпадает ни с одним из концов отрезков, участвующих в пересечении. Отсортируем отезки по углу вокруг <tex>p</tex> и пусть отрезки <tex>s_i</tex> и <tex>s_j</tex> окажутся соседними в сортировке. Тогда существует событие <tex>q</tex> с приоритетом выше, чем у <tex>p</tex>, такое что при обработке этого события <tex>s_i</tex> и <tex>s_j</tex> будут соседними в статусе.
||proof=Рассмотрим событие <tex>q</tex> которое будет обработано непосредственно перед <tex>p</tex>. Предположим, что при обработке этого события <tex>s_i</tex> и <tex>s_j</tex> не будут соседними в статусе. Это возможно только если между ними есть третий отрезок <tex>s</tex>. Но тогда или <tex>s</tex> пересекает какой-то из отрезков <tex>s_i, s_j</tex> в точке с приоритетом выше, чем у <tex>p</tex>, но ниже, чем у <tex>q</tex>; или <tex>s</tex> пересекает их оба в точке <tex>p</tex>, что противоречит тому, что они соседние в сортировке по углу; или конец отрезка имеет приоритет выше, чем <tex> p </tex>, но ниже, чем <tex> q </tex> и отрезок не пересекает <tex>s_i, s_j</tex>. Тогда в последнем случае нарушается тот факт, что <tex>q</tex> идет непосредственно перед <tex>p</tex>. Следовательно при обработке <tex>q</tex> <tex>s_i</tex> и <tex>s_j</tex> будут соседними в статусе.
}}
Очевидно, что статус в худшем случае занимает <tex>O(n)</tex> памяти, однако очередь событий может занимать <tex>O(n + I)</tex> памяти. Если модифицировать алгоритм так, что в очереди будут храниться только точки пересечения отрезков, соседних в статусе, то объём используемой памяти сократится до <tex>O(n)</tex>. Для этого нужно удалять точки пересечения отрезков, которые перестали быть соседними. Перед тем, как мы дойдём до точки удаленной точки, обязательно найдётся соседняя в статусе пара отрезков, которые пересекаются в этой точке, и она снова будет вставлена в очередь. Ясно, что такая оптимизация не влияет на время работы алгоритма.
 
== См. также ==
* [[Алгоритм Бентли-Оттмана]]
== Источники ==
1632
правки

Навигация