|
Построение выпуклой оболочки на плоскости
Алгоритм |
Время - все точки внутри |
Время - все точки на оболочке |
Среднее время |
Оптимальность/ примечания |
|
O( n ) |
O( n2 ) |
O( nh ) |
Простой принцип работы. h - количество углов оболочки. |
|
O( n ) |
O( nlogn ) |
O( nlogn ) |
в среднем быстрейший |
|
O( nlogn ) |
O( nlogn ) |
O( nlogn ) |
Время тормозится сортировкой. Сам алгоритм всегда O( n ) |
|
O( n ) |
O( n ) |
O( n ) |
Вводимые точки должны образовывать ломаную без самопересечений. Если это не так, пользуйтесь другими алгоритмами. Впрочем, этого можно добиться за O(n), например, поразрядной сортировкой сначала по x, затем по y |
Gift Wrapping
Этот алгоритм обводит точки линией, как будто это - кусок веревки. На рисунке ниже дана подробная иллюстрация его действия.
Псевдокод
Находим нижнюю-правую точку. Пусть это - i(0). i=i(0).
Повторять :
- Для каждого j != i вычисляем точку с наименьшим углом от предыдущей стороны ( для второй точки - от горизонтали ). Если есть две таких - берем ту, до которой расстояние больше. Пусть ее номер - k.
- Выводим сторону из точек с номерами i и k. i = k.
пока i не станет равно i(0).
QuickHull
Этот алгоритм начинает работу с создания четырехугольника, соединяющего крайние точки ( смотри шаг А ). Только точки, лежащие вне его могут лежать на оболочке и будут рассматриваться в дальнейшем. Каждый лежащий вне четырехугольника участок будет рекурсивно обработан функцией Quickhull. На диаграмме ниже изображена обработка верхнего-правого угла.
На шаге А также находится точка точка с, наиболее удаленная от линии ( a , b ). Следующим шагом мы определяем два множества точек: cправа или на линии ( a , c ) и справа или на ( c , b ). Для них вновь запускаем функцию Quickhull.
Ниже изображена аналогичная обработка первого множества. Находится точка c' ( cм. С ), наиболее удаленная от линии (a',b'), определяются два новых множества точек: справа или на (a',c') и (c',b')... Получается D, которое обрабатывается дальше..
.
Алгоритм продолжает вызывать Quickhull для этих двух множеств. На рис. E и F показан результат вызова для первого множества. Если множество состоит только из двух точек, рекурсия останавливается, возвращая эти две точки как сторону выпуклой оболочки ( Шаг F, сторону изображает черная линия ).
Затем обрабатывается второе множество ( Шаг G ), и опять рекурсия останавливается, добравшись до двух точек. И так продолжается, пока не обработан весь верхний-правый угол. После этого ( Шаг H )у нас готова выпуклая оболочка для этого региона.
Аналогичным способом обрабатываются верхний-левый, нижний-левый и нижний-правый углы, пока не получим полную выпуклую оболочку ( Шаг I ).
Псевдокод
Находим левейшую, правейшую, самую нижнюю и самую верхнюю точки
Соединяем их четырехугольником
Запускаем QuickHull для четырех треугольных регионов вне четырехугольника
function QuickHull(a,b,s)
if S={a,b} then return(a,b)
else
c = index of right of
(a,c)
A = точки справа от
(a,c)
B = точки справа от
(a,b)
return QuickHull(a,c,A)
объединенное с QuickHull(c,b,B)
Алгоритм Грэхема.
Первым шагом определяем самую нижнюю-правую точку, пусть это - точка 0. Она наверняка лежит на оболочке ( Шаг А ).
Следующая стадия - сортировка всех точек по углу между осью Х и линией ( 0 , эта точка ). Точка с самым большим углом отправляется в стек за точкой 0 ( Шаг B ).
Дальше все точки обрабатываются по очереди, начиная от самого меньшего угла ( точка 1 ) и до тех пор, пока не достигнута точка 9. Процесс состоит из теста, является ли строго левым поворот к новой точке на пути: верхняя-1 точка стека - верхняя точка стека - тестируемая точка. Если это так, тогда она идет в стек и переходим к следующей точке. Если нет - то убираем вершину стека и повторяем проверку с той же точкой. Это проиллюстрировано в шагах C-L. Иногда из стека приходтся убирать несколько точек подряд, так как они последовательно не проходят проверки: обнаруживается правый поворот. Вообще говоря, в алгоритме еще нужна проверка на то, нет ли поворота _обратно_, и в этом случае точку следует оставить: мы имеем прямую. Но это уже мелочи.
Псевдокод
Найти нижнюю ( правую ) точку.
Пометить ее p(0)
Отсортировать все остальные точки по углу относительно p(0)
Если одинаковый угол - по расстоянию.
помечаем их p(1),...,p(n-1)
Стек S=(p(n-1),p(0))=(p(t-1),p(i)); t - индекс вершины
i = 1
while i < n do
if p(i) строго слева от ( p(t-1),p(t) )
then Push(S,i) and i = i + 1
else Pop(S)
Алгоритм Мелькмана.
Этот алгоритм за O( n ) времени 'выправляет' впуклости многоугольника, то есть замкнутой непересекающейся ломаной, строя таким образом выпуклую оболочку.
Что интересно, точки могут поступать по мере выполнения алгоритма, и их количество может быть заранее не известно. Главное, чтобы ломаная не пересекалась.
Для начала объявим вспомогательную функцию, которая для любой тройки x, y, z будет >0, =0 или <0 в зависимости от того, находится ли z справа, на одной линии или слева от линии ( x, y ).
Пусть ломаная задана точками v1, ..., vm.
Алгоритм начинает с пустого дека D. Операции:
push v - положить элемент на верх дека,
insert v - положить элемент под низ дека,
pop - убрать верхний элемент дека,
remove - удалить нижний элемент дека.
Псевдокод
Шаг 1:
Ввод v1, v2, v3;
if (v1,v2,v3)>0
then {push v1; push v2;}
else {push v2; push v1;}
push v3; insert v3;
Шаг 2:
Ввод v;
until ((v,db,db+1)<0 or (dt-1,dt,c)<0) do { ввод v };
Шаг 3:
until ((dt-1,dt,v)>0) do pop;
push v;
Шаг 4:
until ((v,db,db+1)>0) do remove;
insert v;
Идти на Шаг 2.
Реализация на Си
| |