Изменения

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

Skip quadtree: определение, время работы

9486 байт добавлено, 19:19, 4 сентября 2022
м
rollbackEdits.php mass rollback
{{ready}}
== Описание ==
[[Файл:Skip_quadtree.png | 500px | thumb | По картинке должно быть понятно]]Skip quadtree {{---}} как skip list, только вместо list'а quadtree. Поэтому желательно знатьструктура данных, что такое напоминающая [[Список с пропусками | skip -list]], которая позволяет эффективно локализоваться и необходимо знать, что такое [[Квадродеревья#Сжатое квадродерево | сжатое квадродерево]]. В данной статье будет рассматриваться только рандомизированая версия этой структуры, потому что больше и не нужно, кажетсяпроизводить операции над множеством точек.
В данной статье будет рассматриваться только рандомизированая версия этой структуры. The randomized skip quadtree {{---}} последовательность сжатых квадродеревьев над последовательностью подмножеств некоторого исходного множества точек <tex>S</tex>. <tex>S_0 = S</tex>, в <tex>S_1S_i</tex> каждый элемент из <tex>S_0S_{i-1}</tex> входит с вероятностью <tex>p \in (0, 1)</tex> и так далее, то есть <tex>S_i \subset S_{i-1}</tex>. The randomized skip quadtree состоит из последовательности <tex>\{Q_i\}</tex>, где <tex>Q_i</tex> {{---}} [[Квадродеревья#Сжатое квадродерево | сжатое квадродерево ]] над множеством <tex>S_i</tex>. Будем называть эти квадродеревья уровнями, при этом нулевой уровень содержит в точности точки из <tex>S</tex>.ЗаметимНа рисунке представлено дерево, состоящее из трех слоев: <tex>Q_0</tex>, <tex>Q_1</tex>, <tex>Q_2</tex>. Одинаковые вершины, находящиеся на разных уровнях, соединены линиями. Стрелками обозначены переходы в процессе локализации при поиске точки <tex>y</tex>. Любая вершина однозначно определяется своими координатами на каждом уровне. Поэтому по вершине с уровня <tex>i</tex> мы можем получить ее на уровне <tex>i - 1</tex>, что если какой-то квадрат она [[Квадродеревья и перечисление точек в произвольном прямоугольнике (статика)#interesting | интересныйинтересная]] на уровне <tex>i</tex>, так как в противном случае она могла быть сжата.  Заметим, что если какой-то квадрат интересный в <tex>Q_i</tex>, то он интересный и в <tex>Q_{i-1}</tex>, так как <tex>S_i \subset S_{i-1}</tex> по определению структуры.
== Операции над skip quadtree ==
Будем для каждого интересного квадрата Для реализации операций нам нужно уметь получать по вершине с уровня <tex>i</tex> соответствующую ей вершину с уровня <tex>i - 1</tex>. Сделать это можно двумя способами:* Хранить в вершине ссылку на каждом уровне хранить указатели на тот же квадрат вершину уровнем ниже и уровнем выше (если есть).* Так как каждая вершина однозначно задается своими координатами, можем сопоставить ей маску. Используем ассоциативный массив, чтобы по маске получать ссылку на вершину. Такие массивы будем хранить для каждого уровня skip quadtree.
===Локализация===Локализация выполняется аналогично сжатому квадродеревулокализации в сжатом квадродереве. Под локализацией подразумевается, что мы хотим найти минимальный интересный квадрат, геометрически содержащий данную точку (содержит геометрически, в самом дереве её может не быть, тут, возможно, правильнее сказать «пересекает»). Сначала локализуемся в квадродереве наибольшего уровня, начиная с его корня. Затем локализуемся в квадродереве уровня уровнем ниже, начиная уже не с корня, а с того квадрата, который нашли на прошлом уровне. И так далееНо на каждом уровне, кроме нулевого, локализумся не до листа, а до глубочайшего интересного. Продолжаем, пока не дойдём до днанулевого уровня.
Для добавления ===Вставка===При вставке сначала надо локализоваться. При этом мы локализуемся сразу на всех уровнях (так уж устроен процесс). Дальше добавляемся в , запоминая ссылки, дальше добавляем вершину на нулевой уровень, затем с вероятностью <tex>p</tex> добавляемся на уровень выше и так далее до первого недобавления. При этом количество уровней должно может увеличиться максимум на <tex>1</tex>, то есть, если появился новый уровень, то процесс точно заканчивается. Хотя не, давайте без последнего условия, вроде с ним только лучше, но без него проще доказывать.
===Удаление совсем просто: ===При удалении локализуемся, и удаляем вершину со всех уровней, на которых она есть, не забывая обновлять ссылки или ассоциативный массив. При этом какой-то уровень мог стать пустым, в таком случае выкинем его.
== Время работы и память ==
<tex>p_{m - j}</tex> был последним из <tex>p_0, p_1, \dots, p_m</tex> интересным квадратом в <tex>Q_{i + 1}</tex> небходимо, чтобы среди этих как минимум <tex>j + 1</tex> непустых четвертинок только одна (вероятность этого назовём <tex>Pr_1</tex>) или ноль (вероятность этого назовём <tex>Pr_0</tex>) были непустыми в <tex>Q_{i + 1}</tex>. Иначе, если будет хотя бы пара непустых четвертинок, то их наименьший общий предок в дереве будет интересным квадратом и будет находиться глубже <tex>p_{m - j}</tex>. Таким образом, искомая вероятность не превосходит <tex>Pr_0 + Pr_1</tex>.
''Лично мне утверждение из предыдущего абзаца далось с трудом, если у вас тоже всё очень плохо, попробуйте напрячь мозг и залипнуть в картинку, вдруг поможет. Хотя я постарался расписать поподробней, чем в статье.''
<tex>q = 1 - p</tex>
<tex>Pr_1 \leq (j + 1) \cdot pq^j</tex>, потому что это в сущности вероятность того, что ровно одна точка из как минимум <tex>j + 1</tex> непустых четвертинок попала на уровень выше.
''В общем, если чуть подумать, оценки на <tex>Pr_0</tex> и <tex>Pr_1</tex> довольно ясны.''
<tex>E(j) = \sum\limits_{j = 1}^{m} j \cdot Pr(j) \leq \sum\limits_{j = 1}^{m} j \cdot (q^{(j + 1)} + (j + 1) \cdot pq^j) \leq \sum\limits_{j = 1}^{\infty} j \cdot (q^{(j + 1)} + (j + 1) \cdot pq^j)</tex>
Математическое ожидание количества уровней составляет <tex>O(\log(n))</tex>
|proof=
Для оценки мат. ожидания посчитаем вероятность того, что количество уровней <tex>h</tex> равно <tex>k</tex>. <tex>p(h = k) = p(h \leq k) \cdot p(h \geq k)</tex>.
<tex>p(h \leq = k) = (1 - p^{(h > k + 1})^n</tex>, потому что вероятность того, что точка дойдёт до уровня - p(h <tex>k + 1</tex>, равна <tex>p^{k + 1})</tex>.
<tex>p(h \geq < k) = (1 - (1 - p^{k})^n)</tex>, потому что вероятность того, что точка не дойдёт до уровня <tex>k</tex>, равна <tex>1 - p^{k}</tex>.
''А вот нифига не так<tex>p(h > k) = (1 - (1 - p^{k + 1})^n)</tex>, я тут понял. Там зависимые событияпотому что вероятность того, поэтому перемножать вероятности так нельзячто точка не дойдёт до уровня <tex>k + 1</tex>, но всё не сильно портитсяравна <tex>1 - p^{k + 1}</tex>.  <tex>p(h = k) = 1 - p(h > k) - p(h < k) = 1 - (1 - (1 - p^{k + 1})^n) - (1 - p^{k})^n = (1 - p^{k + 1})^n - (1 - p^k)^n \leq 1 - (1 - p^k)^n \leq np^k</tex>, и дальше этой оценки достаточно Теперь посчитаем мат.''ожидание количества уровней:
<tex>E(h) = \sum\limits_{k = 1}^{\infty} k \cdot p(h = k) = p(1) \cdot 1 + \dots + p(\log_{1/p} n) \cdot \log_{1/p} n + \sum\limits_{k = \log_{1/p} n + 1}^{\infty} k \cdot p(k)</tex>
Поиск, добавление и удаление точки работают за <tex>O(\log(n))</tex> в среднем.
|proof=
Достаточно очевидно Напрямую следует из двух предыдущих лемм.
}}
}}
== В чём профит? Запрос точек в прямоугольнике==Как и [[Перечисление Задача: нам дается прямоугольник, нужно выдать все точки, лежащие в нем.  Реализация запроса на сжатом квадродереве занимает <tex>O(n)</tex> времени. Используем skip quadtree для ускорения поиска. Для этого ослабим условие задачи, тогда skip quadtree позволит очень быстро (асимптотически) отвечать на такие запросы.  Ослабление: расширим данный прямоугольник на <tex>\varepsilon</tex>.Тогда в ответ могут попасть точки не из данного прямоугольника, но лежащие внутри <tex>\varepsilon</tex>-области. В большинстве практических задач данное ослабление не ухудшит конечный результат, а только ускорит процесс. Skip quadtree позволяет отвечать на запрос всех точек , лежащих в произвольном прямоугольнике , окруженном <tex>\varepsilon</tex>-областью, за n * log <tex>O(\varepsilon^(d {- 1) } \cdot \log n (range tree+ k) </tex>, где <tex>k</tex> {{---}} число точек в ответе. Алгоритм можно модифицировать для ответа на запрос точек в любой выпуклой фигуре. Обозначим наш прямоугольник <tex>R</tex>. Тогда <tex>\varepsilon</tex>-область {{---}} область <tex>E</tex>, охватывающая <tex>R</tex>, граница которой удалена от его сторон на <tex>\varepsilon</tex>. [[Файл:Skip_quadtree_rect.png|right| range tree400px]]Данный прямоугольник <tex>R</tex> разбивает вершины на следующие классы:* <tex>\mathrm{in}</tex> {{---}} ''внутренние'', данная структура умеет выдавать точкито есть лежащие внутри <tex>\varepsilon</tex>-области (1 и 6 на рисунке).* <tex>\mathrm{out}</tex> {{---}} ''внешние'', принадлежащие какомуто есть лежащие вне прямоугольника <tex>R</tex> (2 на рисунке).* <tex>\mathrm{stabbing}</tex> {{-то прямоугольнику--}} ''пронзающие'', пересекающие <tex>R</tex>, но не являющиеся ''внутренними'' (3, алгоритм очень похож 4 и 5 на тот, который применяется в range treeрисунке). Только  Для вершин из классов <tex>\mathrm{in}</tex> и <tex>\mathrm{out}</tex> ответ на запрос происходит за находится тривиально {{---}} все поддерево вершины и никакие точки из поддерева соответственно; сложность представляют вершины из класса <tex>\mathrm{stabbing}</tex>. Зная их все, мы можем ответить на запрос.  Мощность множества ''пронзающих'' вершин может составлять <tex>O(\log n + k)</tex> (вроде бы независимо от размерности, хотя квадродерево подразумевает двумерное пространствотак как ''пронзающие'' вершины могут быть вложены друг в друга, тогда как нам достаточно рассмотреть только наименьшую из вложенных вершин. Назовем ''пронзающую'' вершину ''критической'', если для каждого из ее детей выполняется одно из двух условий:* ребенок не является ''пронзающей'' вершиной.* ребенок является ''пронзающим'', но содержит меньшую часть <tex>E</tex>, чем рассматриваемая вершина.  На рисунке среди вершин 3, 4, 5, 6 только 5 является ''критической''. Вместо поиска всех ''пронзающих'' вершин, для обобщения решения задачи достаточно найти все ''критические'' вершины. {{Лемма|about=Об упаковке|statement=Количество ''критически''х вершин на нулевом уровне дерева равно <tex>O(\varepsilon^{-1})</tex>. |proof=Для квадратов с длиной стороны <tex>\varepsilon</tex> верно, что они либо <tex>\mathrm{in}</tex>, либо <tex>\mathrm{out}</tex>, то есть <tex>\mathrm{stabbing}</tex> вершин с такой стороной не бывает, как и со стороной меньше <tex>\varepsilon</tex>. Все точки, содержащиеся в skip quadtree, находятся внутри какого-то прямоугольника, значит, длина его стороны {{---}} константа. Поэтому длина стороны области пересечения запрашиваемого прямоугольника со skip quadtree {{---}} это тоже константа. Тогда длина стороны запрашиваемого прямоугольника {{---}} <tex>O(1)</tex>. ''Пронзающих'' вершин c длиной стороны чуть больше размерности асимптотика будет та же<tex>\varepsilon</tex>, области которых не пересекаются, кажетсяможет быть <tex>O(\varepsilon^{-1})</tex> {{---}} длина стороны прямоугольника, деленная на <tex>\varepsilon</tex>. И памяти нужно Тогда всего их может быть <tex>\varepsilon^{-1} + \genfrac{}{}{}{0}{1}{2} \cdot \varepsilon^{-1} + \genfrac{}{}{}{0}{1}{4} \cdot \varepsilon^{-1} + \dots \ = \ O(n\varepsilon^{-1})</tex>, так как пронзающие вершины могут быть вложенными, и длина стороны родителя в два раза больше, чем у ребенка, то количество родителей вершин с такой стороной в два раза меньше. Этим skip quadtree круче ''Критических'' вершин не больше, чем ''пронзающих'', так что их тоже <tex>O(\varepsilon^{-1})</tex>.}}
=== Как Алгоритм===Начинаем отвечать на запрос ===с корня <tex>Q_0</tex> и определяем тип вершин.* Если вершина ''внутренняя'', добавляем ее в ответ вместе с поддеревом.* Если ''внешняя'', то игнорируем.* Если ''критическая'', то рассмотрим всех ее детей.Сначала локализуем все четыре точки прямоугольника* Если не ''критическая'', то найдем минимальную ''критическую'', содержащую ту же часть <tex>E</tex>, что и рассматриваемая вершина.
''Я тут понялПервые три варианта рассматриваются тривиально. Покажем, как для данной некритической вершины <tex>p</tex> найти минимальную критическую вершину <tex>q</tex>, содержащую ту же часть <tex>E</tex>, что пока не знаю, как и <tex>p</tex>. Для это делатьнайдем такое <tex>Q_i</tex>, сейчас буду думать.что <tex>p</tex> будет ''критической''вершиной в <tex>Q_i</tex> при максимальном <tex>i</tex>. И будем действовать аналогично процессу локализации.
Таким образом, поиск <tex>q</tex> займет <tex>O(\log n)</tex> времени. А так как ''критических'Всё очень плохо' вершин всего <tex>O(\varepsilon^{-1})</tex>, я тупой, меня можно побитьто итоговая ассимптотика составит <tex>O(\varepsilon^{-1} \cdot \log n + k)</tex>.''
===Псевдокод=== ''Боря тоже не знает'void''' points_in_rectangle('''rectangle''' R, '''rectangle''' E, так что я не тупой'''vector<point>&''' points) '''queue<node>''' que que.push(<tex>Q_0</tex>.root) '''while''' (!que.empty()) '''node''' n = que.pop() '''rectangle''' K // прямоугольник, меня не надо битьсоответсвующий вершине n '''if''' (R <tex>\cap</tex> K == <tex>\varnothing</tex>) '''continue''' '''else if''' (n is leaf) '''if''' (n.point in R) points.push_back(n.point) '''else if''' (K <tex>\subset</tex> E) n. А ещё выяснилосьadd_subtree(points) // добавляем все точки поддерева ''внутренней'' вершины '''else if''' (n is not critical) '''node''' q // некритическая вершина на максимальном уровне, соответствующая n level = k - 1 // максимальный уровень, на котором вершина q некритическая // k - количество уровней в skip quadtree '''while''' (level > 0) node <tex>n_{level}</tex> = n from <tex>Q_{level}</tex> // вершина, соответствующая n в дереве <tex>Q_{level}</tex> '''if''' (<tex>n_{level}</tex> != null '''and''' <tex>n_{level}</tex> is not critical) q = <tex>n_{level}</tex> '''break''' level-- '''while''' (true) '''if''' (q is not critical) '''node''' qc = ребенок q, что от нас это не требуетсясодержащий ту же область E, так что особо любознательные могут сами подумать, а остальные могут просто забитьи q '''if''' (qc is not leaf '''or''' level == 0) q = qc '''else if''' (level > 0) level-- q = q from <tex>Q_{level}</tex> '''else if''' (level != 0) level-- q = q from <tex>Q_{level}</tex> '''else''' '''break''' que.push(q) '''else''' que.add_all(n.children)
== Источник ==
* http://arxiv.org/pdf/cs.cg/0507049.pdf* http://www.ics.uci.edu/~goodrich/pubs/skip.pdf
[[Категория: Вычислительная геометрия]]
[[Категория: Структуры данных]]
1632
правки

Навигация