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