Алгоритм Балабана — различия между версиями
(→Основной алгоритм: Приподнял картинку) |
(→Основной алгоритм) |
||
| Строка 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 Найдем используя двоичный поиск; Найдем используя значения, полученные шагом выше; ;
Примечания
Литература
Т.Вознюк, В.Терещенко — К построению эффективного решения задачи пересечения отрезков
Ф.Препарата, М.Шеймос — Вычислительная геометрия