Алгоритм Балабана — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Search In Strip)
(Перевод)
Строка 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>, таким образом, число дополнительных отрезков, появляющихся после ''разрезаний'' пропорционально числу найденных пересечений.
  
===Пункт подробностей =)===
+
===Основной алгоритм===
  
 
Давайте разберемся с алгоритмом более подробно:
 
Давайте разберемся с алгоритмом более подробно:
  
Не умаляя общности, предположим, что все пересечения и вершины отрезков имеют разные абсциссы (в конечном счете, их можно будет отсортировать введением дополнительных свойств). Будем рассматривать целые координаты на промежутке <tex>\[1, 2N\]</tex>. Пусть <tex>p_i{</tex> и <tex>s(i)</tex> будут координатами вершин <tex>i</tex>-того отрезка.(???)
+
Не умаляя общности, предположим, что все пересечения и вершины отрезков имеют разные абсциссы (в конечном счете, их можно будет отсортировать введением дополнительных свойств). Будем рассматривать целые координаты на промежутке <tex>[1, 2N]</tex>. Пусть <tex>p_i</tex> и <tex>s(i)</tex> будут координатами вершин <tex>i</tex>-того отрезка.(???)
  
 
Основная задача нашего алгоритма, это рекурсивная функция <tex>TreeSearch</tex>. Мы соединяем каждый вызов функции с узлом некоего двоичного дерева (далее ''рекурсивное дерево''). Мы отмечаем все значения, множества и параметры вызова соответствующим узлом. В результате, мы проанализируем наш алгоритм рекурсивного дерева. Обозначим множество всех вершин рекурсивного дерева за <tex>RT</tex>, а множество внутренних вершин за <tex>V</tex>. (WAT??)
 
Основная задача нашего алгоритма, это рекурсивная функция <tex>TreeSearch</tex>. Мы соединяем каждый вызов функции с узлом некоего двоичного дерева (далее ''рекурсивное дерево''). Мы отмечаем все значения, множества и параметры вызова соответствующим узлом. В результате, мы проанализируем наш алгоритм рекурсивного дерева. Обозначим множество всех вершин рекурсивного дерева за <tex>RT</tex>, а множество внутренних вершин за <tex>V</tex>. (WAT??)
  
 
  <tex>IntersectingPairs(S_v, a, b):</tex>
 
  <tex>IntersectingPairs(S_v, a, b):</tex>
     Отсортировать <tex>2*N</tex> вершин по координатам и
+
     Отсортируем <tex>2 \cdot N</tex> вершин по координатам и
         найти <tex>p_i, s(i), i = 1,...,2*N; S_r \leftarrow S_0</tex>
+
         найдем <tex>p_i, s(i), i = 1,...,2 \cdot N;\ S_r \leftarrow S_0</tex>
     <tex>TreeSearch(S_r, 1, 2*N)</tex>;
+
     <tex>TreeSearch(S_r, 1, 2 \cdot N)</tex>;
  
 
  <tex>TreeSearch(S_r, a, b):</tex>
 
  <tex>TreeSearch(S_r, a, b):</tex>
 +
<tex>\{</tex>
 
     '''if''' <tex>b - a = 1</tex> '''then'''
 
     '''if''' <tex>b - a = 1</tex> '''then'''
 
     <tex>\{</tex>
 
     <tex>\{</tex>
         <tex>L \leftarrow sort S_v by <_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>;  
         break;
+
         return;
 
     <tex>\}</tex>
 
     <tex>\}</tex>
Split S. into Q. and S: so that staircase
+
    Разделим <tex>S_v</tex> на <tex>Q_v</tex> и <tex>S_v'</tex> так, что лестница
Du := (Qv, (b, e)) be complete relative to S:;
+
        <tex>D_v := (Q_v, \langle a, b \rangle)</tex> будет полностью соотносима множеству <tex>S_v'</tex>;
Find lnt(DV , S:);
+
    Найдем <tex>Int(D_v, S_v')</tex>;
c:= L(b + e)/2J;
+
    <tex>c := \lfloor (a + b)/2 \rfloor</tex>;
Place segments of S:
+
    Разделим отрезки из <tex>S_v'</tex> на пересекающих
crossing the strip (b, c) into S[~(V ) and
+
        полосу <tex>\langle a, c \rangle</tex> <tex>S_{ls}(v)</tex> и
the strip (c, e) into s~.(~);
+
        полосу <tex>\langle c, b \rangle</tex> <tex>S_{rs}(v)</tex>;
TreeSearch(S1.(V), b, c);
+
    <tex>TreeSearch(S_{ls}(v), a, c)</tex>;
TreeSearch(ST,(V), c, e);
+
    <tex>TreeSearch(S_{rs}(v), c, b)</tex>;
end procedure.
+
<tex>\}</tex>
 +
 
 +
Отсюда и дальше <tex>ls(v)</tex>, <tex>rs(v)</tex> и <tex>ft(v)</tex> означают, соответственно, левого сына, правого сына, и отцовскую вершину узла <tex>v<.tex>.
  
 
==Примечания==
 
==Примечания==

Версия 15:24, 13 ноября 2013

Эта статья находится в разработке!

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

Введение

Решение задачи по поиску множества пересечений отрезков является одной из главных задач вычислительной геометрии. Тривиальный детерминированный алгоритм имеет временную сложность O(n2), и его суть заключается в проверке попарного пересечения отрезков. Сложнее, но эффективнее алгоритм Бентли-Оттмана [1] с оценкой сложности O((n+k) log(n)+k), в основе которого лежит метод заметающей прямой. Алгоритм, предложенный Чазелле и Едельсбруннером [2], имеет лучшую оценку O(n log(n)+k), но в отличие от предыдущих методов требует квадратичной памяти. Оптимальный детерминированный алгоритм был предложен Балабаном [3] с временной оценкой сложности O(n log(n)+k) и O(n) памяти, где К - число пересекающихся отрезков. При количестве отрезков равным 2000 и большому количеству пересечений целесообразно использовать алгоритм Балабана. Однако в результате громоздкости и высокой сложности реализации алгоритма в большинстве практических задач используется алгоритм заметающей прямой Бентли-Оттмана.

Основные понятия

Введем некоторые обозначения. Пусть Int(S) - множество всех точек пересечения отрезков из множества S, а K=|Int(S)| - количество таких пересечений ;
Через a,b обозначим вертикальную полосу, которая ограничена прямыми x=a и x=b, а через s — отрезок с вершинами в точках с абсциссами l и r.
Рассмотрим взаимное расположение вертикальной полосы a,b и отрезка s.


Определение:
Будем говорить, что отрезок s, с вершинами в точках с абсциссами l и r :

- содержит(span) полосу a,b, если labr;
- внутренний(inner) для полосы a,b, если a<l<r<b;

- пересекает(cross) полосу a,b в других случаях.


Определение:
Два отрезка s1 и s2 называются пересекающимися внутри полосы a,b, если их точка пересечения лежит в пределах этой полосы.
Для двух множеств отрезков S и S определим множество Int(S,S) как {s,s|(sS,sS)&(s intersect s)}.
D=((s1,s2,s3),a,b), Loc(D,s4)=0, Loc(D,s5)=2 или 3, Int(D,{s4, s5})={{s3, s5}}

Обозначения Inta,b(S) и Inta,b(S,S) будут использоваться для описания подмножеств Int(S) и Int(S,S), состоящих из пересекающихся пар отрезков в пределах полосы a,b. Далее скобки {} используются для определения неупорядоченных наборов, а скобки () используются для определения упорядоченных множеств.

Введем отношение порядка на множестве отрезков s1<bs2 если оба отрезка пересекают вертикальную линию x=a и точка пересечения этой прямой с отрезком s1 лежит ниже точки пересечения с s2.


Определение:
Лестница D — это пара (Q,a,b), в которой отрезки из множества Q удовлетворяют следующим условиям :

− любой отрезок из Q содержит полосу a,b;
− нет пересечений отрезков внутри лестницы;
Q упорядочена по отношению <a.

Часть отрезков лестницы внутри полосы будем называть ступеньками.


Определение:
Будем называть лестницу D полностью соотносимой множеству отрезков S, если каждый отрезок из S либо не пересекает полосу a,b, либо пересекает хотя бы одну из ступенек из множества D.


Лемма:
Если лестница D полностью соотносима множеству отрезков S, где S состоит из отрезков, пересекающих полосу a,b, тогда |S|Endsa,b(S)+|Int(D,S)|,
где Endsa,b(S) это число вершин отрезков S, находящихся в пределах полосы a,b.


Определение:
Если точка p отрезка s лежит между ступеньками i и i+1, тогда число i называется местоположением s на лестнице D и обозначается как Loc(D,s)


Утверждение:
Имея лестницу D=(Q,a,b) и множество отрезков S, множество Int(D,S) можно найти за время O(|S|log|Q|+|Int(D,S)|).
Однако, если S упорядочено отношением <x, где x[a,b], тогда можно найти Int(D,S) за время O(|S|+|Q|+|Int(D,S)|).

Алгоритм

Введем несколько дополнительных функций, чтобы упростить основной алгоритм:

Split

Функция Split разделяет входное множество отрезков L, пересекающих некоторую полосу a,b, на подмножества Q и L так, что лестница (Q,a,b) полностью соотносима множеству отрезков L.

Пусть L=(s1,...,sk), где si<bsi+1
Splita,b(L,Q,L)
{
    L;Q
    for j=1,...,k do
        if отрезок Sj не пересекает
        последний отрезок из Q внутри полосы a,b
        и при этом содержит её then
            добавить sj в конец Q;
        else
            добавить sj в конец L;
}

Эта функция работает за O(|L|) времени.

Search In Strip

Зная L мы можем найти Inta,b(L) и R используя следующую рекурсивную функцию:

SearchInStripa,b(L,R)
{
    Split(L,Q,L); 
    if L= then
        RQ; 
        return;
    Найдем Inta,b(Q,L);
    SearchInStripa,b(L,R);
    RMergeb(Q,R);
}

Здесь, Mergex(S1,S2) это функция объединения множеств S1 и S2, упорядоченных по отношению <x. Время выполнения SearchInStrip эквивалентно сумме времён каждого её запуска. Очевидно, что время работы i-той функции, будет равно O(|Li|+|Inta,b(Qi,Li)|), где Li,Qi,Li это соответствующие наборы (L0=L,Li+1=Li).

Учитывая лемму, заключаем, что функция SearchInStripa,b(L,R) работает за O(|L|+|Inta,b(L)|).

Предположим, что все отрезки лежат в полосе a,b. Таким образом в самом начале у нас есть пара (S,a,b,). Что же дальше происходит: множество S распадается в подмножества Q и S, после чего лестница D=(Q,a,b) становится полностью соотносимой множеству S. Необходимо найти пересечения отрезков из D и S, затем, все пересечения в S. Чтобы найти пересечения отрезков в S, мы режем полосу a,b и множество S по вертикале x=c на полосы a,c, c,b и множества Sls, Srs соответственно, где c является медианой вершин отрезков, между a и b. Затем мы рекурсивно вызываем функцию к парам (Sls,a,c) и (Srs,c,b). Ключевым является тот факт, что согласно лемме |S|Endsa,b(S)+|Int(D,S)|, таким образом, число дополнительных отрезков, появляющихся после разрезаний пропорционально числу найденных пересечений.

Основной алгоритм

Давайте разберемся с алгоритмом более подробно:

Не умаляя общности, предположим, что все пересечения и вершины отрезков имеют разные абсциссы (в конечном счете, их можно будет отсортировать введением дополнительных свойств). Будем рассматривать целые координаты на промежутке [1,2N]. Пусть pi и s(i) будут координатами вершин i-того отрезка.(???)

Основная задача нашего алгоритма, это рекурсивная функция TreeSearch. Мы соединяем каждый вызов функции с узлом некоего двоичного дерева (далее рекурсивное дерево). Мы отмечаем все значения, множества и параметры вызова соответствующим узлом. В результате, мы проанализируем наш алгоритм рекурсивного дерева. Обозначим множество всех вершин рекурсивного дерева за RT, а множество внутренних вершин за V. (WAT??)

IntersectingPairs(Sv,a,b):
    Отсортируем 2N вершин по координатам и
        найдем pi,s(i),i=1,...,2N; SrS0
    TreeSearch(Sr,1,2N);
TreeSearch(Sr,a,b):
{
    if ba=1 then
    {
        L отсортируем Sv по отношению <b;
        SearchInStripa,b(Lv,Rv); 
        return;
    }
    Разделим Sv на Qv и Sv так, что лестница
        Dv:=(Qv,a,b) будет полностью соотносима множеству Sv;
    Найдем Int(Dv,Sv);
    c:=(a+b)/2;
    Разделим отрезки из Sv на пересекающих
        полосу a,c Sls(v) и
        полосу c,b Srs(v);
    TreeSearch(Sls(v),a,c);
    TreeSearch(Srs(v),c,b);
}

Отсюда и дальше ls(v), rs(v) и ft(v) означают, соответственно, левого сына, правого сына, и отцовскую вершину узла <tex>v<.tex>.

Примечания

Литература

Т.Вознюк, В.Терещенко — К построению эффективного решения задачи пересечения отрезков
Ф.Препарата, М.Шеймос — Вычислительная геометрия