Изменения
→Задача
Упрощение полигональной цепи {{---}} процесс, позволяющий уменьшить число точек полилинии.
==Задача==
Дана некоторая полилиния, заданная последовательностью точек <tex> a_1, a_2, ..., a_n</tex>, и некоторое <tex>\varepsilon</tex>. Требуется найти цепь <tex> a_1, a_i, a_j, ..., a_n </tex>, являющейся подпоследовательностью исходной, для которой . Для этой цепи верно, что для всех <tex>k</tex>, таких что <tex> i < k < j:</tex> <tex>\rho(a_k, Segment(a_i, a_j)) \le \varepsilon</tex> для любых соседних <tex>i</tex> и <tex>j</tex>.
Существует также альтернативная задача, в которой вместо <tex>\varepsilon</tex> задано число <tex>k</tex> вершин в итоговой цепи, . В этом случае требуется составить цепь <tex> a_1, a_i, a_j, ..., a_n </tex> заданной длины таким образом, что максимально необходимое <tex>\varepsilon</tex> для условия <tex> \rho(a_k, Segment(a_i, a_j)) \le \varepsilon</tex> было минимально.
Также упрощение можно выполнять с помощью введения новой полилинии, не сохраняющей точки исходной, но такая вариация задачи рассмотрена не будет.
Такая задача встречается при обработки векторной графики и построении карт. Например, есть цепь, несколько точек которой попадают в один и тот же пиксель. Очевидно, что тогда можно упростить все эти точки в одну. В этом случае и пригодится упрощение, одним из вариантов реализации которого является алгоритм Дугласа-Пекера (Douglas-Peucker).
В случае, когда у нас есть несколько устройств с разным dpi, например, монитор и принтер, то, взяв за <tex>\varepsilon</tex> половину расстояния, которое помещается на одной из границ пикселя, мы можем адаптировать растеризовать одну и ту же цепь для разных устройств.
==Алгоритм Дугласа-Пекера==
Алгоритм рекурсивно делит полилинию. Входом алгоритма служат координаты всех точек между первой и последней включая их, а так же <tex>\varepsilon</tex>. Первая и последняя точка сохраняются неизменными. После чего алгоритм находит точку, наиболее удалённую от отрезка, состоящего из первой и последней (оптимальный способ поиска расстояния от точки до отрезка рассмотрен ниже). Если точка находится на расстоянии, меньше, чем <tex>\varepsilon</tex>, то все точки, которые ещё не были отмечены к сохранению, могут быть выброшены из набора, и получившаяся прямая сглаживает кривую с точностью не ниже <tex>\varepsilon</tex>.
Если же расстояние больше <tex>\varepsilon</tex>, то алгоритм рекурсивно вызывает себя на наборе от начальной до данной и от данной до конечной точек (это означает, что данная точка будет отмечена к сохранению).
По окончанию всех рекурсивных вызовов итоговая полилиния строится только из тех точек, что были отмечены к сохранению.
| <tex>9</tex> || Алгоритм завершен
|}
Полилиния, полученная в результате работы алгоритма, отражается черной пунктирной линией.
===Время работы===
Ожидаемая сложность алгоритма может быть оценена выражением <tex>\Theta(n\log n)</tex> в лучшем случае, когда номер наиболее удаленной точки всегда оказывается лексикографически центральным. Однако, в худшем случае сложность алгоритма составляет <tex>O(n^2)</tex>. Это достигается, апримернапример, в случае, если номер наиболее удаленной точки всегда соседний к номеру граничной точки.
===Замечания к алгоритму===
К сожалению, алгоритм Дугласа-Пекера в ходе своей работы не сохраняет топологию. Это означает, что в ответе мы можем получить линию с самопересечениями.
В статье де Берга (de Berg) ''[http://www.bowdoin.edu/~ltoma/teaching/cs350/spring06/Lecture-Handouts/deberg95new.pdf "A New Approach to Subdivision Simplification"]''(1995) приведен алгоритм, позволяющий решать чуть более общую задачу, чем текущая: упрощение полигональной цепи с учетом обязательных особых точек, не входящих в нее и с учетом топологии. Можно использовать алгоритм и для текущего случая, задав множество особых точек пустым. Время работы алгоритма при этом составит <tex>O(n^2\log n)</tex>. Краткое описание алгоритма дано ниже.
====Оптимальность====
===Решение альтернативной задачи===
====Реализация====
Практически очевидно, что данный вариант алгоритма легко реализовать на приоритетной очереди. Будем хранить расстояние, на котором находится наиболее удаленная вершина, как ключ, а номера вершин-границ как значение. На каждой итерации мы выбираем обьект с наибольшим ключем, делим и получившиеся части кладем обратно в очередь с новыми ключами.
===Реализация===
[[Файл:DistancePtoSDistancePtoS1.png|400px|right]]Даны точка <tex>(x_0, y_0)O</tex> и отрезок, заданный точками <tex>(x_1, y_1)A</tex> и <tex>(x_2, y_2)B</tex>.
Введём обозначения:
*<tex>R_1</tex> {{---}} отрезок <tex>[(x_0, y_0)O; (x_1, y_1)A]</tex>*<tex>R_2</tex> {{---}} отрезок <tex>[(x_0, y_0)O; (x_2, y_2)B]</tex>*<tex>R_{12}</tex> {{---}} отрезок <tex>[(x_1, y_1)A; (x_2, y_2)B]</tex>
Если:
*<tex>|R_1| \ge \sqrt{|R_2|^2+|R_{12}|^2}</tex>, то ответ это <tex>|R_2|</tex>, так как угол между <tex>R_2</tex> и <tex>R_{12}</tex> при данном условии <tex> \ge 90^{\circ}</tex>
*<tex>|R_2| \ge \sqrt{|R_1|^2+|R_{12}|^2}</tex>, то ответ это <tex>|R_1|</tex>, так как угол между <tex>R_1</tex> и <tex>R_{12}</tex> при данном условии <tex> \ge 90^{\circ}</tex>
*Оба предыдущих условия ложны, то <tex>\operatorname{abs} (\frac{| \overrightarrow{R_{12}} \times \overrightarrow{R_1}|}{|\overrightarrow{R_{12}}|})</tex>, где <tex> \overrightarrow{R_{12}} = (x_2 B -x_1, y_2 - y_1)A</tex> и <tex> \overrightarrow{R_1} = (x_1 - x_0, y_1 A - y_0)O</tex>. Это следует из формул для площади параллелограмма через векторное произведение и через произведения основания на высоту
<br clear="all"/>
==Обзор ускорения работы алгоритма Дугласа-Пекера==
Как описано выше, в худшем случае алгоритм работает за <tex>O(n^2)</tex>. Можно внести дополнения, которые позволят получить <tex>O(n\log n)</tex> в худшем случае. Ускорение основывается на уменьшении времени поиска наиболее удаленной вершины. Это можно осуществить благодаря идее о том, что наиболее удаленная вершина лежит на выпуклой оболочке полигональной цепи. Описание ускорения взято из статьи Хершберга(Hershberger) ''[http://www.bowdoin.edu/~ltoma/teaching/cs350/spring06/Lecture-Handouts/hershberger92speeding.pdf "Speeding Up the Douglas-Peucker Line-Simplification Algorithm"]'' (1992).
Для построения выпуклой оболочки используется [http://www.ams.sunysb.edu/~jsbm/courses/545/melkman.pdf алгоритм Мелкмана], который работает для двумерной полигональной цепи без самопересечений за <tex>O(n)</tex>, строя все промежуточные выпуклые оболочки, которые пригодятся в дальнейшем.
Алгоритм делится на четыре основных этапа:
*Создание графа <tex>G_1</tex>, в котором помимо исходных ребер, добавлены все возможные сокращенные ребра. Иначе говоря ребра, для который верно, что <tex>i < j</tex> и для любого <tex>t</tex>, такого что <tex>i < t < j</tex>, верно <tex>distance\rho(V_t, \overline{V_iV_j}) \le \varepsilon</tex>.
*Создание графа <tex>G_2</tex>, в котором добавлены все возможные ребра, не нарушающие топологию. На этом этапе построение выполняется без учета <tex>\varepsilon</tex>.
*Создание графа <tex> G = G_1 \cap G_2</tex>, в котором будет содержаться кратчайшая полигональная цепь без самопересечений по построению <tex>G_1</tex> и <tex>G_2</tex>.
*[http://pers.narod.ru/algorithms/pas_dist_from_point_to_line.html Поиск расстояния от точки до отрезка]
*[http://algolist.manual.ru/maths/geom/distance/pointline.php Поиск расстояния от точки до прямой]
*[http://www.bowdoin.edu/~ltoma/teaching/cs350/spring06/Lecture-Handouts/hershberger92speeding.pdf "Speeding Up the Douglas-Peucker Line-Simplification Algorithm"]
*[http://www.ams.sunysb.edu/~jsbm/courses/545/melkman.pdf Алгоритм Мелкмана]
*[http://softsurfer.com/Archive/algorithm_0203/algorithm_0203.htm Алгоритм Мелкмана]
*[http://softsurfer.com/Archive/algorithm_0112/algorithm_0112.htm Двоичный поиск на выпуклой оболочке]
*[http://www.bowdoin.edu/~ltoma/teaching/cs350/spring06/Lecture-Handouts/deberg95new.pdf "A New Approach to Subdivision Simplification"]
*[http://www.codeproject.com/Articles/114797/Polyline-Simplification#headingRD Алгоритмы и визуализатор упрощения полигональной цепи]
[[Категория: Вычислительная геометрия]]