Трапецоидная карта
Трапецоидная карта - геометрическая структура позволяющая локализоваться на площади за
.Постановка задачи
Предположим, у нас есть наши координаты, и есть карта мира.
Мы можем найти по карте наше местоположение и сказать в какой стране мы находимся. Области задаются замкнутыми ломаными.
Формальная постановка задачи
Есть множество отрезков на плоскости. Есть запрос (точка q), на выходе — область, в которой находится точка q.
Структура данных
- Геометрическая
У нас есть множество отрезков ограничееных оболочкой R(это не выпуклая оболочка, а просто мнимая граница плоскости за которую не вылезают отрезки)
Мы договариваемся что никакие две точки не лежат на одной вертикале(в противном случаи все еще противнее)
Трапецоидная карта множества отрезков S - это эти отрезки + из каждой точки выпущены два луча, вверх и вниз до первого пересечения с другим отрезком или с оболочкой R.
Лемма: |
Любой face трапецоидной карты ограничен одним или двумя вертикальными отрезками и обязательно двумя не вертикальными отрезками. |
Именно отсюда берется название структуры, так как любой face либо трапеция, либо треугольник.
Введем обозначения для навигации по карте.
- левая граница(leftp) - точка определяющая левую сторону трапецоида или в случаи треугольника просто являющаяся левой вершиной.
- правая граница(rightp) - аналогично левой только справа.
- верхний отрезок(top) и нижний отрезок(bottom) - отрезки ограничивающие трапецоид сверху и снизу.
- трапецоиды называются смежными, если имеют общую вертикальную границу.
- пусть смежны и либо top( ) = top( ), либо bottom( ) = bottom( )
Тогда
, называют либо нижними левыми соседями, либо верхними.
Теорема: |
Трапецоидная карта построенная на n отрезках содержит максимум 6n+4 вершины и максимум 3n+1 трапецоид. |
Доказательство: |
|
Хранить трапецоиды можно в чем угодно. Вместе с самим трапецоидом, стоит хранить leftp, rightp, top и bottom так же следует хранить соседей трапецоида.
- Поисковая структура
Поисковая структура представляет из себя ациклический граф с одним корнем и каждому трапецоиду в структуре соответствует один лист.
У каждого узла есть два ребенка и при этом узел может быть двух типов.
- Первый тип узла - точка, соответствующая концу отрезка.
- Второй тип узла - отрезок.
Во время запроса мы двигаемся по графу от его корня до момента, когда окажемся в листе, это и будет означать что точка находится внутри трапецоида.
Если мы находимся не в листе, то мы должны опрелетиться в каком из детей мы окажемся дальше.
Еcть два правила:
- Если текущий узел соответсвует вершине, то смотрим левее или провее мы находимся(проверка по x-координате).
- Если текущий узел соответствует отрезку, то смотрим выше или ниже мы находимся(проверка по y-координате).
- Плохие случаи:
Мы находимся на одной вертикале с вершиной
Мы находимся на отрезке
(Решение: молиться, или просто обрабатывать вручную.)
Алгоритм
Во время построения трапецоидной карты(в дальнейшем
) алгоритм так же строит структуру для поиска .Так как трапецоидная карта - геометрическая структура, а основные операции ведутся именно с поисковой, упор на неё.
Наш алгоритм добавляет отрезки по одному и после каждого добававления модидицирует
и .Порядок добавления отрезков
От порядка добавления зависит время запроса. Как? Время запрос пропорцианально глубине графа.
Считается, что еслли добавлять отрезки рандомно, то время будет хорошим. Почему и какое время будет рассписано дальше.
Алгоритм
- Добавили отрезок.
- Нашли все трапецоиды, которые пересек новый отрезок.
- Удалили их.
- Создали новый трапецоиды.
Поиск трапецоидов которых пересек отрезок
Чтобы модифицировать карту, мы должны понять где произошло изменение.
Оно произошло в тех трапецоидах которые пересек текущий отрезок или можно сказать, что трапецоид с i-1-ой итерации не будет в i-ой только если его пересек отрезок.
Пусть якобы есть множество трапецоидов
упорядоченное поПусть
один из правых соседей Так же при этом не сложно понять каким соседом он является.Если rightp(
) лежит выше , то сосед нижний и наоборот.Это значит, что если мы знаем первый трапецоид, то мы можем найти остальные просто обходя по карте соседей справа.
Чтобы найти первый трапецоид нужно просто локализоваться правому концу в текущей карте.
update
Рассмотрим подробнее последние две части
Есть два случая.
- Простой - отрезок не пересекает ни одного трапецоида, то есть целеком внутри.
Тогда удаляем этот старый трапецоид и на его место ставим дерево из двух концов отрезка, отрезка и четырех образовавшихся трапецоидов.
Важно не забыть правильно определить соседей новых трапецоидов.
В случаи если какие-то трапецоиды выродятся в треугольники будет не четыре новых трапецоида , а 2 или 3. Слава богу это не самая большая проблема.
- Сложный - отрезок пересекает сразу несколько трапецоидов.
Итак наш отрезок пересекает трапецоиды
.Сначала добавляем вертикальные лучи из концов текущего отрезка. Это нужно, чтобы модифицировать
. Теперь мы должны удалить соответствующие листья и на новые которые появились из-за изменения лучей.Дальше мы модифицуруем вертикальные лучи, которые пересекают текущий отрезок. Этот процесс происходит достаточно быстро, так мы храним много информацию об этих лучах в
По хорошему то как этот происходит просто ужасно и видеть это не хочется, а все потому, что много что добавляется много новых узлов.
.
Асимптотика
Запрос
Предположим у нас есть запрос на локализацию точки q. Время затраченное на этот запрос будет линейно зависеть от глубина графа.
При добавлении в карту очередного отрезка(в дальнейшем итерация алгоритма) глубина графа увеличивается максимум на 3. Из этого мы можем сделать простую оценку.
Наибольшее время на запрос, которое мы можем потратить — 3n. Произойдет это в самом ужасном из самых ужасных случаев.
Как говорилось раньше отрезки мы добавляем рандомно, а потому редко будет самый ужасный случай и с вероятностных точек зрения время на запрос будет меньше.
Рассмотрим путь пройденный точкой по графу. Каждый узел был создан на какой-то итерации цикла. Обозначим за
- количество узлов созданных на итерации i.Так как никто не выбирал исходное множество отрезков и запрос q,
- рандомная величина зависящая только от рандомного порядка добавления отрезков.
Как уже упоминалось на каждой итерации добавляется не более 3 узлов, а значит
<= 3.Считая, что
- вероятность того, что существует узел, который встречается при нашем запросе созданный на i-ой итерации.
Начинаем оценивать
.Что значит, что узел был создан на i-ой итерации и встретился при запросе q?
Это значит, что на i - 1-ой итерации мы локализовывали q в трапецоиде
,а на i-ой итерации уже в трапецоиде
и эти два трапецоида разные.То есть, после добавления непонятно чего в карту, трапецоид изменился.
Таким образом P_i = P(
).Если эти два трапеецоида не равны, значит на i-ой итерации трапецоид
был одним из созданных при модификации.Заметим, что все трапецоиды созданные на этой итерации были смежны текущему отрезку(
).Значит либо
= top( ) или bottom( ), либо концы = leftp( ) или rightp( ).Зафиксируем множество отрезков на i-ой итерации. Тогда состояние трапецоидов никак не будет зависеть от порядка добавленных отрезков.
Тогда, вероятность изменения трапецоида - это его вероятность исчезнуть если удалится
.Тогда переходим, к top(
) и т.п. так как мы уже говорили, что будет определенной стороной при навигации.Отрезки добавлялись рандомно поэтому в качестве
мог быть любой отрезок из . А тогда вероятность для всех сторон 1/i.Суммируем по всем 4 сторонам.
Таким образом P_i = P(
) = P( ) <= 4/i<=
Память
Заметим, что количество трапецоидов как мы доказали раньше равно O(n), поэтому мы должны оценить количество узлов созданых на i-ой итерации.
А результирующее выражение для памяти тогда будет
Mem = O(n) +
количество узлов созданное на i-ой итерацииОбозначив за k_i количество узлов созданное на i-ой итерации
Mem = O(n) +
E[k_i]Используя вывод из предыдущей части получаем, что E[k_i] <= O(i)/i = O(1)
А тогда Mem = O(n)
Из этих двух выводов очевидно следует, что время построения карты равно O(nlogn)
Реализация
Здесь будут рассмотрены некоторые основные моменты реализации Это только идейные реализации в коде все выглядет пример в 50 раз хуже.(по количеству строк)
Класс "трапецоид"
struct Trapezoid
Trapezoid next Trapezoid up Trapezoid down Trapezoid end Segment top Segment bottom Point left Point right
Построение трапецоидной карты
TrapezoidMap(S - segments)
Строим оболочку(просто находим крайние точки множества отрезков по четырем направлениям) Строим рандомную перестановку отрезков for i1 to n do ищем множество трапецоидов пересекаемых отрезком . //это специальная функция// Удаляем это множество из карты и добавляем новые узлы появившиеся из-за в поисковой структуре Аналогично для просто карты
Поиск трапецоидов, которых пересекает отрезок
LookforTrapezoid(
- segment)Запоминаем левый и правый конецДелаем запрос на левый конец в карте. j 0; while q правый от rightp(трапецоид_j) do if rightp(трапецоид_j) над then ставим трапецоид_(j+1) нижним правым соседом трапецоид_j. else ставим трапецоид_(j+1) верхним правым соседом трапецоид_j. j j+1