Алгоритм Балабана — различия между версиями
(→Основной алгоритм: Приподнял картинку) |
(→Основной алгоритм) |
||
Строка 116: | Строка 116: | ||
Что же дальше происходит: множество <tex>S</tex> ''распадается'' в подмножества <tex>Q</tex> и <tex>S'</tex>, после чего лестница <tex>D = (Q, \langle a, b \rangle)</tex> становится полностью соотносимой множеству <tex>S'</tex>. Необходимо найти пересечения отрезков из <tex>D</tex> и <tex>S'</tex>, затем, все пересечения в <tex>S'</tex>. Чтобы найти пересечения отрезков в <tex>S'</tex>, мы ''режем'' полосу <tex>\langle a, b \rangle</tex> и множество <tex>S'</tex> по вертикале <tex>x = c</tex> на полосы <tex>\langle a, c \rangle</tex>, <tex>\langle c, b \rangle</tex> и множества <tex>S'_{ls}</tex>, <tex>S'_{rs}</tex> соответственно, где c является медианой вершин отрезков, между <tex>a</tex> и <tex>b</tex>. Затем мы рекурсивно вызываем функцию к парам <tex>(S'_{ls}, \langle a, c \rangle)</tex> и <tex>(S'_{rs}, \langle c, b \rangle)</tex>. Ключевым является тот факт, что согласно [[#lemma1|лемме]] <tex>|S'| \le Ends_{a, b}(S') + |Int(D, S')|</tex>, таким образом, число дополнительных отрезков, появляющихся после ''разрезаний'' пропорционально числу найденных пересечений. | Что же дальше происходит: множество <tex>S</tex> ''распадается'' в подмножества <tex>Q</tex> и <tex>S'</tex>, после чего лестница <tex>D = (Q, \langle a, b \rangle)</tex> становится полностью соотносимой множеству <tex>S'</tex>. Необходимо найти пересечения отрезков из <tex>D</tex> и <tex>S'</tex>, затем, все пересечения в <tex>S'</tex>. Чтобы найти пересечения отрезков в <tex>S'</tex>, мы ''режем'' полосу <tex>\langle a, b \rangle</tex> и множество <tex>S'</tex> по вертикале <tex>x = c</tex> на полосы <tex>\langle a, c \rangle</tex>, <tex>\langle c, b \rangle</tex> и множества <tex>S'_{ls}</tex>, <tex>S'_{rs}</tex> соответственно, где c является медианой вершин отрезков, между <tex>a</tex> и <tex>b</tex>. Затем мы рекурсивно вызываем функцию к парам <tex>(S'_{ls}, \langle a, c \rangle)</tex> и <tex>(S'_{rs}, \langle c, b \rangle)</tex>. Ключевым является тот факт, что согласно [[#lemma1|лемме]] <tex>|S'| \le Ends_{a, b}(S') + |Int(D, S')|</tex>, таким образом, число дополнительных отрезков, появляющихся после ''разрезаний'' пропорционально числу найденных пересечений. | ||
− | === | + | ===Основы алгоритма=== |
Давайте разберемся с алгоритмом более подробно: | Давайте разберемся с алгоритмом более подробно: | ||
Строка 135: | Строка 135: | ||
<tex>L \leftarrow</tex> отсортируем <tex>S_v</tex> по отношению <tex><_b</tex>; | <tex>L \leftarrow</tex> отсортируем <tex>S_v</tex> по отношению <tex><_b</tex>; | ||
<tex>SearchInStrip_{a, b}(L_v, R_v)</tex>; | <tex>SearchInStrip_{a, b}(L_v, R_v)</tex>; | ||
− | return; | + | '''return'''; |
<tex>\}</tex> | <tex>\}</tex> | ||
Разделим <tex>S_v</tex> на <tex>Q_v</tex> и <tex>S_v'</tex> так, что лестница | Разделим <tex>S_v</tex> на <tex>Q_v</tex> и <tex>S_v'</tex> так, что лестница | ||
− | <tex>D_v | + | <tex>D_v \leftarrow (Q_v, \langle a, b \rangle)</tex> будет полностью соотносима множеству <tex>S_v'</tex>; |
Найдем <tex>Int(D_v, S_v')</tex>; | Найдем <tex>Int(D_v, S_v')</tex>; | ||
− | <tex>c | + | <tex>c \leftarrow \lfloor (a + b)/2 \rfloor</tex>; |
Разделим отрезки из <tex>S_v'</tex> на пересекающих | Разделим отрезки из <tex>S_v'</tex> на пересекающих | ||
полосу <tex>\langle a, c \rangle</tex> <tex>S_{ls}(v)</tex> и | полосу <tex>\langle a, c \rangle</tex> <tex>S_{ls}(v)</tex> и | ||
Строка 157: | Строка 157: | ||
Чтобы решить эту проблему, представим <tex>S_v</tex> как объединение трех множеств: | Чтобы решить эту проблему, представим <tex>S_v</tex> как объединение трех множеств: | ||
множества <tex>L_v</tex> упорядоченного по отношению <tex><_a</tex>, неупорядоченного множества <tex>I_v</tex>, и множества <tex>R_v</tex> упорядоченного по отношению <tex><_b</tex>. Расположим отрезки из <tex>S_v</tex>, пересекающие границу <tex>x = a</tex> во множество <tex>L_v</tex>, отрезки пересекающие <tex>x = b</tex> во множество <tex>R_v</tex>, и внутренние отрезки во множество <tex>I_v</tex> (пример на рисунке справа). | множества <tex>L_v</tex> упорядоченного по отношению <tex><_a</tex>, неупорядоченного множества <tex>I_v</tex>, и множества <tex>R_v</tex> упорядоченного по отношению <tex><_b</tex>. Расположим отрезки из <tex>S_v</tex>, пересекающие границу <tex>x = a</tex> во множество <tex>L_v</tex>, отрезки пересекающие <tex>x = b</tex> во множество <tex>R_v</tex>, и внутренние отрезки во множество <tex>I_v</tex> (пример на рисунке справа). | ||
+ | |||
+ | Теперь мы можем вызвать функцию <tex>Split</tex> для множества <tex>L_v</tex> и построить <tex>Q_v</tex> за <tex>O(|L_v|) = O(|S_v|)</tex> времени. Но мы натыкаемся на новую проблему: передавая множества <tex>L_v</tex>, <tex>I_v</tex> и <tex>R_v</tex>, необходимо найти соответствующие множества сыновей узла <tex>v</tex>. | ||
+ | |||
+ | Неупорядоченные множества <tex>I_{ls(v)}</tex> и <tex>I_{rs(v)}</tex> строятся легко. Множество <tex>L_{ls(v)}</tex> будет найдено вызовом функции <tex>Split_{a, b}(L_v, Q_v, L_{ls(v)})</tex> для третьего шага функции <tex>TreeSearch</tex>. Множество <tex>L_{rs(v)}</tex> получается из <tex>R_{ls(v)}</tex> за линейное время вставкой (если <tex>p_c</tex> левый конец отрезка) или удалением (если <tex>p_c</tex> правый конец отрезка) отрезка <tex>s(c)</tex>. Но как получить <tex>R_{ls(v)}</tex> из <tex>L_v</tex>, <tex>R_v</tex> и <tex>I_v</tex> без сортировки? | ||
+ | |||
+ | Для листьев мы сделаем проверку вначале, и получим <tex>R_v</tex> из <tex>L_v</tex>. Пусть <tex>L_v</tex> и <tex>I_v</tex> известны, и все сыновья узла <tex>v</tex> - листья. Для начала запустим функцию <tex>Split(L_v, Q_v, L_{ls(v)})</tex> и найдем <tex>Q_v</tex> и <tex>L_{ls(v)}</tex>. Теперь мы должны найти <tex>Int(D_s, S_v') = Int(D_v, L_{ls(v)}) \cup Int(D_v, I_v) \cup Int(D_v, R_{rs(v)})</tex>, но мы не знаем <tex>R_{rs(v)}</tex>, и соответственно можем найти только <tex>Int(D_v, L_{ls(v)}) \cup Int(D_v, I_v)</tex>. Применим <tex>SearchInStrip</tex> к множеству <tex>L_{ls(v)}</tex> и получим <tex>R_{ls(v)}</tex>. Множество <tex>L_{rs(v)}</tex> получается из <tex>R_{ls(v)}</tex> вставкой или удалением отрезка <tex>s(c)</tex>. Применим <tex>SearchInStrip</tex> к <tex>L_{rs(v)}</tex> и найдем <tex>R_{rs(v)}</tex>. Теперь можем продолжить вычисление <tex>Int(D_v, R_{rs(v)})</tex> и получим <tex>R_v</tex> слиянием <tex>Q_v</tex> и <tex>R_{rs(v)}</tex>. | ||
+ | |||
+ | Конечная функция будет выглядеть так: | ||
+ | <tex>IntersectingPairs(S_0)</tex> | ||
+ | <tex>\{</tex> | ||
+ | Отсортируем <tex>2N</tex> концов отрезков по абсциссе | ||
+ | и найдем <tex>p_i</tex>, <tex>s(i)</tex> где <tex>i = 1, ..., 2N</tex>; | ||
+ | <tex>L_r \leftarrow (s(1))</tex>; <tex>I_r \leftarrow S_0 \setminus (\{s(1)\} \cup \{s(2N)\})</tex>; | ||
+ | <tex>TreeSearch(L_r, I_r, 1, 2N, R_r)</tex>; | ||
+ | <tex>\}</tex> | ||
+ | |||
+ | <tex>TreeSearch(L_v, I_v, a, b, R_v)</tex> | ||
+ | <tex>\{</tex> | ||
+ | '''if''' <tex>b - a = 1</tex> '''then''' | ||
+ | <tex>\{</tex> | ||
+ | <tex>SearchInStrip_{a, b}(L_v, R_v)</tex>; | ||
+ | '''return'''; | ||
+ | <tex>\}</tex> | ||
+ | <tex>Split_{a, b}(L_v, Q_v, L_{ls(v)})</tex>; | ||
+ | <tex>D_v \leftarrow (Q_v, \langle a, b \rangle)</tex>; | ||
+ | Найдем <tex>Int(D_v, L_{ls(v)})</tex>; | ||
+ | <tex>c \leftarrow \lfloor (a + b) / 2 \rfloor</tex>; | ||
+ | Разделяем отрезки из <tex>I_v</tex> | ||
+ | внутренние для полосы <tex>\langle a, c \rangle</tex> во множество <tex>I_{ls(v)}</tex> | ||
+ | внутренние для полосы <tex>\langle c, b \rangle</tex> во множество <tex>I_{rs(v)}</tex> | ||
+ | <tex>TreeSearch(L_{ls(v)}, I_{ls(v)}, a, c, R_{ls(v)})</tex>; | ||
+ | '''if''' <tex>p_c</tex> левый конец отрезка <tex>s(c)</tex> '''then''' | ||
+ | <tex>L_{rs(v)} \leftarrow</tex> вставить <tex>s(c)</tex> в <tex>R_{ls(v)}</tex> | ||
+ | '''else''' | ||
+ | <tex>L_{rs(v)} \leftarrow</tex> удалить <tex>s(c)</tex> из <tex>R_{ls(v)}</tex> | ||
+ | <tex>TreeSearch(L_{rs(v)}, I_{rs(v)}, c, b, R_{rs(v)})</tex>; | ||
+ | Найдем <tex>Int(D_v, R_{rs(v)})</tex>; | ||
+ | '''for''' <tex>s \in I_v</tex> '''do''' | ||
+ | Найдем <tex>Loc(D_v, s)</tex> используя двоичный поиск; | ||
+ | Найдем <tex>Int(D_v, I_v)</tex> используя значения, полученные шагом выше; | ||
+ | <tex>R_v \leftarrow Merge_b(Q_v, R_{rs(v)})</tex>; | ||
+ | <tex>\}</tex> | ||
==Примечания== | ==Примечания== |
Версия 00:44, 15 ноября 2013
Алгоритм Балабана — детерминированный алгоритм, позволяющий по множеству отрезков на плоскости получить множество точек, в которых эти отрезки пересекаются.
Содержание
Введение
Решение задачи по поиску множества пересечений отрезков является одной из главных задач вычислительной геометрии. Тривиальный детерминированный алгоритм имеет временную сложность [1] с оценкой сложности , в основе которого лежит метод заметающей прямой. Алгоритм, предложенный Чазелле и Едельсбруннером [2], имеет лучшую оценку , но в отличие от предыдущих методов требует квадратичной памяти. Оптимальный детерминированный алгоритм был предложен Балабаном [3] с временной оценкой сложности и памяти, где К - число пересекающихся отрезков. При количестве отрезков равным 2000 и большому количеству пересечений целесообразно использовать алгоритм Балабана. Однако в результате громоздкости и высокой сложности реализации алгоритма в большинстве практических задач используется алгоритм заметающей прямой Бентли-Оттмана.
, и его суть заключается в проверке попарного пересечения отрезков. Сложнее, но эффективнее алгоритм Бентли-ОттманаОсновные понятия
Введем некоторые обозначения. Пусть
Через обозначим вертикальную полосу, которая ограничена прямыми и , а через — отрезок с вершинами в точках с абсциссами и .
Рассмотрим взаимное расположение вертикальной полосы и отрезка .
Определение: |
Будем говорить, что отрезок - содержит(span) полосу | , с вершинами в точках с абсциссами и :
Определение: |
Два отрезка Для двух множеств отрезков и определим множество как . | и называются пересекающимися внутри полосы , если их точка пересечения лежит в пределах этой полосы.
Обозначения
и будут использоваться для описания подмножеств и , состоящих из пересекающихся пар отрезков в пределах полосы . Далее скобки используются для определения неупорядоченных наборов, а скобки используются для определения упорядоченных множеств.Введем отношение порядка на множестве отрезков
если оба отрезка пересекают вертикальную линию и точка пересечения этой прямой с отрезком лежит ниже точки пересечения с .
− любой отрезок из
− нет пересечений отрезков внутри лестницы;
− упорядочена по отношению .
Определение: |
Будем называть лестницу | полностью соотносимой множеству отрезков , если каждый отрезок из либо не пересекает полосу , либо пересекает хотя бы одну из ступенек из множества .
Лемма: |
Если лестница полностью соотносима множеству отрезков , где состоит из отрезков, пересекающих полосу , тогда ,где это число вершин отрезков , находящихся в пределах полосы . |
Определение: |
Если точка | отрезка лежит между ступеньками и , тогда число называется местоположением на лестнице и обозначается как
Утверждение: |
Имея лестницу и множество отрезков , множество можно найти за время . Однако, если упорядочено отношением , где , тогда можно найти за время . |
Алгоритм
Введем несколько дополнительных функций, чтобы упростить основной алгоритм:
Split
Функция
разделяет входное множество отрезков , пересекающих некоторую полосу , на подмножества и так, что лестница полностью соотносима множеству отрезков .Пусть, где for do if отрезок не пересекает последний отрезок из внутри полосы и при этом содержит её then добавить в конец else добавить в конец
Эта функция работает за
времени.Search In Strip
Зная
мы можем найти и используя следующую рекурсивную функцию:if then return Найдем
Здесь,
это функция объединения множеств и , упорядоченных по отношению . Время выполнения эквивалентно сумме времён каждого её запуска. Очевидно, что время работы -той функции, будет равно , где это соответствующие наборы .Учитывая лемму, заключаем, что функция работает за .
Предположим, что все отрезки лежат в полосе лемме , таким образом, число дополнительных отрезков, появляющихся после разрезаний пропорционально числу найденных пересечений.
. Таким образом в самом начале у нас есть пара . Что же дальше происходит: множество распадается в подмножества и , после чего лестница становится полностью соотносимой множеству . Необходимо найти пересечения отрезков из и , затем, все пересечения в . Чтобы найти пересечения отрезков в , мы режем полосу и множество по вертикале на полосы , и множества , соответственно, где c является медианой вершин отрезков, между и . Затем мы рекурсивно вызываем функцию к парам и . Ключевым является тот факт, что согласноОсновы алгоритма
Давайте разберемся с алгоритмом более подробно:
Не умаляя общности, предположим, что все пересечения и вершины отрезков имеют разные абсциссы (в конечном счете, их можно будет отсортировать введением дополнительных свойств). Будем рассматривать целые координаты на промежутке
. Пусть и будут координатами вершин -того отрезка.Основная задача нашего алгоритма, это рекурсивная функция
. Мы соединяем каждый вызов функции с узлом некоего двоичного дерева (далее рекурсивное дерево). Мы отмечаем все значения, множества и параметры вызова соответствующим узлом. В результате, мы проанализируем наш алгоритм рекурсивного дерева. Обозначим множество всех вершин рекурсивного дерева за , а множество внутренних вершин за .Отсортируем вершин по координатам и найдем ;
if then отсортируем по отношению ; ; return; Разделим на и так, что лестница будет полностью соотносима множеству ; Найдем ; ; Разделим отрезки из на пересекающих полосу и полосу ; ; ;
Отсюда и дальше
, и означают, соответственно, левого сына, правого сына, и отцовскую вершину узла . Наша задача показать, что все операции с узлом происходят за , и чтобы показать это, возьмем во внимание, что (очевидно, что ).Функция
похожа на функцию . Основная разница заключается в том, что вызывает себя без изменения полосы, когда делит полосу на две части, после чего рекурсивно вызывает себя для них. Другое отличие заключается в том, что множество не упорядочено так же, как . В результате мы не можем напрямую использовать функцию для эффективного деления .Чтобы решить эту проблему, представим
как объединение трех множеств: множества упорядоченного по отношению , неупорядоченного множества , и множества упорядоченного по отношению . Расположим отрезки из , пересекающие границу во множество , отрезки пересекающие во множество , и внутренние отрезки во множество (пример на рисунке справа).Теперь мы можем вызвать функцию
для множества и построить за времени. Но мы натыкаемся на новую проблему: передавая множества , и , необходимо найти соответствующие множества сыновей узла .Неупорядоченные множества
и строятся легко. Множество будет найдено вызовом функции для третьего шага функции . Множество получается из за линейное время вставкой (если левый конец отрезка) или удалением (если правый конец отрезка) отрезка . Но как получить из , и без сортировки?Для листьев мы сделаем проверку вначале, и получим
из . Пусть и известны, и все сыновья узла - листья. Для начала запустим функцию и найдем и . Теперь мы должны найти , но мы не знаем , и соответственно можем найти только . Применим к множеству и получим . Множество получается из вставкой или удалением отрезка . Применим к и найдем . Теперь можем продолжить вычисление и получим слиянием и .Конечная функция будет выглядеть так:
Отсортируем концов отрезков по абсциссе и найдем , где ; ; ; ;
if then ; return; ; ; Найдем ; ; Разделяем отрезки из внутренние для полосы во множество внутренние для полосы во множество ; if левый конец отрезка then вставить в else удалить из ; Найдем ; for do Найдем используя двоичный поиск; Найдем используя значения, полученные шагом выше; ;
Примечания
Литература
Т.Вознюк, В.Терещенко — К построению эффективного решения задачи пересечения отрезков
Ф.Препарата, М.Шеймос — Вычислительная геометрия