Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
67
Добавлен:
01.05.2014
Размер:
1.03 Mб
Скачать

Для построения верхней оболочки используется подмножество точек из S, которые находятся выше линии, соединяющая a и b. Для простоты, также назовем это подмножество S, а его размер будем обозначать n. Если n=2, то верхняя оболочка состоит из одного ребра (a, b), так что будем считать, что n>2.

Для построения верхней оболочки точки qi множества S упорядочивается в соответствии с возрастанием абсциссы и к полученному списку применяется метод обхода Грэхема. Для этого алгоритм Эндрю использует стек x0, x1,…, xt для хранения текущей верхней оболочки. Точка xt считается находящейся на вершине стека. После окончания работы алгоритма стек содержит верхнюю оболочку множества S.

Более точное описание алгоритма таково: инвариантом при любом s является тот факт, что точки x0, x1,…, xt из стека образуют такую подпоследовательность множества qn, q1, q2,…,qs, что:

1)t 2, s 2, x0 = qn = b, x1 = q1 = a, xt = qs ,

2)x1, x2,…, xt является верхней оболочкой q1, q2,…,qs,

3)точки x1, x2,…, xt отсортированы слева направо.

В стадии инициализации в стек последовательно помещаются точки qn , q1 , q2 . Далее, по-

следовательно выбирая по одной точке из множества {qs+1, qs+2,…,qn-1}, где s – это индекс последней проверенной точки (изначально s=2). На каждом этапе проверяется взаимное расположение точек qs+1, xt, xt-1. Пока эти точки не образуют левый поворот, верхняя точка стека удаляется. Как только точки qs+1, xt, xt-1 стали образовывать левый поворот, точка qs+1 помещается в стек и алгоритм переходит к рассмотрению следующей точки. Легко показать, что инвариант выполняется на всем протяжении выполнения алгоритма. Конец алгоритма.

Рисунок 6. Иллюстрация построения верхней оболочки в методе Эндрю

В сущности, алгоритм Эндрю является частным случаем алгоритма Грэхема, когда центральная точка выбирается бесконечно удаленной в отрицательном направлении по оси ординат, так что в этом случае упорядоченность по абсциссе совпадает с упорядоченностью по полярному углу.

Как и алгоритм Грэхема, этот алгоритм имеет трудоёмкость О(n log n). Трудоемкость сортировки множества S при построении каждой из оболочек (верхней и нижней) составляет О(n log n). Процедура построения оболочки занимает время О(n), т.к. очевидно, что каждая точка помещается и извлекается из стека не более одного раза.

11

Достоинства:

Алгоритм Эндрю имеет гарантированную линейно-логарифмическую трудоемкость в худшем случае.

Алгоритм является более простым в реализации, чем алгоритм Грэхема.

Недостатки:

Алгоритм не является открытым.

Алгоритм не имеет обобщения на пространства размерности больше двух. 2.1.4 Алгоритм типа «Разделяй и властвуй» (Divide and conquer)

Алгоритм построения выпуклой оболочки, основанный на методе «Разделяй и властвуй» описан в [7] Ф. Препаратой.

В данном алгоритме множество S разбивается на два примерно равномощных подмножества S’ и S’’, выпуклые оболочки которых не пересекаются Разбиение множества S происходит таким образом: сначала определяется размах множества точек по ординатам и абсциссам. Деление происходит по координате, размах по которой больше. Назовем эту координату экстремальной. Затем за время O(n) строится медиана исходного множества S. Пусть pm – медиана множества S. Далее множество S разбивается на две части: в одной содержатся точки, у которых значение экстремальной координаты больше, чем значение этой координаты у точки pm, в другой половине содержатся точки, у которых значение экстремальной координаты меньше, чем значение этой координаты у точки pm, и сама точка pm. После деления алгоритм запускается рекурсивно для каждой из половин. В случае, когда количество точек в подмножествах невелико, используется любой более простой способ построения выпуклой оболочки.

Быстродействие метода в целом немало зависит от эффективности нахождения слияния двух выпуклых оболочек. Пусть у нас есть выпуклые непересекающиеся многоугольники P’ и P’’, заданные списками своих вершин, отсортированных в порядке против часовой стрелки. Нам требуется найти P – их слияние, которое и будет являться выпуклой оболочкой исходного множества. Для этого находятся крайние точки каждого многоугольника a и b (самая правая для левого, самая левая для правого соответственно). После этого путем последовательных проверок выпуклости строятся верхняя и нижняя касательные к многоугольникам. Это занимает не больше n проверок. Алгоритм построения нижней касательной таков:

1)Пока a и b не являются нижней гранью, повторяем шаги a-b:

a.Пока точки p’a+1, p’a, p’’b не образуют левого поворота, передвигаем a по P’ по часовой стрелке.

b.Пока точки p’a, p’’b, p’’b+1, не образуют левого поворота, передвигаем b по P’’против часовой стрелки.

2)Возвращаемся на шаг 1.

Как только будут выполняться условия левого поворота и для точек 2-го условия, и для точек 3-го условия, поиск нижней касательной закончен. Конец алгоритма.

Очевидно, что трудоемкость такого поиска линейна относительно суммы количеств вершин в объединяемых оболочках.

12

Рис. 7. Построение нижней касательной

Общую трудоемкость метода «разделяй и властвуй» можно выразить как

c if n =1 T (n) cn + 2T (n / 2) if n >1

Развернув эту рекуррентную последовательность, получим

T (n) cn + 2T (n / 2)

cn + 2(cn / 2 + 2T (n / 4))

=2cn + 22 T (n / 22 )

...

icn + 2i T (n / 2i )

где i – глубина рекурсии. Очевидно, что максимальная глубина равна log n. Таким образом,

T (n) cnlog n + nT (1) = O(n log n)

Достоинства:

Гарантированная логарифмическая трудоемкость.

Алгоритм можно распараллеливать.

Алгоритм обобщаем на произвольную размерность.

Недостатки:

Алгоритм относительно сложен в реализации по сравнению с другими.

2.1.5 Алгоритм «быстрого построения» (QuickHull)

Для построения выпуклой оболочки были созданы алгоритмы, напоминающие быструю сортировку. Такие алгоритмы называются быстрыми методами построения оболочки. Одними из первых такой алгоритм предложили Эдди в [8] и Бикат в [9].

13

Рис. 8. Пример работы алгоритма быстрого построения выпуклой оболочки.

Суть алгоритма состоит в том, что исходное множество S из n точек разбивается на два подмножества, каждое из которых будет содержать одну из двух ломаных, которые при соединении образуют выпуклую оболочку. Для начала нужно определить две точки, которые будут являться соседними вершинами выпуклой оболочки. Можно взять самую левую (a) и самую правую (b) вершины. После чего нужно найти точку c максимально удаленную от прямой ab.

Алгоритм выбора с основывается на том факте, что эта точка определяет треугольник, попадая в который, будут отбрасываться точки. Для того, чтобы максимизировать число точек, подлежащих удалению, разумным было бы найти точку c такой, чтобы она максимизировала площадь треугольника abc. Одновременно с тем, точка c должна лежать по левую сторону от отрезка ab. Таким образом совместив эти два условия, определяем, что за точка c выбирается та точка из S \ {a,b}, которая максимизирует значение (a,b,c) .

Все точки, лежащие в треугольнике abc исключаются из дальнейшего рассмотрения. Для того, чтобы на каждом шаге исключать как можно больше точек, точка c выбирается по критерию наибольшей площади треугольника abc. Остальные точки будут делиться на два подмножества: точки, которые лежать левее ac и точки, которые лежат правее и cb. Каждое из них содержит ломаные которые в сочетании с a, b и c дают выпуклую оболочку. С каждым из них проделываем то же самое. В подмножестве точек , лежащих левее cb выбираем c’ по тем же признакам, по которым выбирали точку c, максимально удаленную от cb, которая делит его на три части. Из них одна выбрасывается, а остальные делятся опять. Это реализуется рекурсивной процедурой, которая для данного ей множества возвращает соответствующую часть выпуклой оболочки. Конец алгоритма.

В случае, когда мощность каждого, из подмножеств, на которое делится множество, не превосходит некоторой константы умноженной на мощность множества, получаем сложность алгоритма, как и в быстрой сортировке O(n log n). Но в худшем случае может потребоваться время O(n2).

Достоинства:

Алгоритм можно распараллеливать.

Алгоритм обобщаем на произвольную размерность.

Недостатки:

Высокая квадратичная трудоемкость в худшем случае.

2.1.6 Алгоритм Чена До сих пор было рассмотрено два типа алгоритмов построения выпуклой оболочки:

14

1.Алгоритм Грэхема, его модификации, алгоритм «разделяй и властвуй», быстрый алгоритм построения выпуклой оболочки. Эти алгоритмы имеют трудоемкость O(n log n)

2.Обход Джарвиса. Если за h обозначить количество вершин выпуклой оболочки, то этот алгоритм имеет трудоемкость O(nh). Такие алгоритмы, трудоемкость которых зависит и от n (размера входных данных), и от h (размера выходных данных), называются чувстви-

тельными к результату.

Стоит заметить две вещи. Во-первых, алгоритмы первого типа оптимальны в среднем. Однако, если предположить, что h, – количество вершин выпуклой оболочки, мало, то трудоемкость алгоритма Джарвиса меньше, чем у алгоритмов первого типа. К примеру, если h известно заранее или постоянно, то алгоритм Джарвиса имеет линейную трудоемкость, тогда как алгоритмы из первой группы всё равно имеют логарифмическую трудоемкость. С другой стороны, если h гораздо больше, чем log n, то алгоритм Джарвиса проигрывает. К примеру, если h=n, то этот алгоритм показывает квадратичную трудоемкость.

Эти рассуждения наводят на следующую проблему: существует ли алгоритм, по крайней мере такой же быстрый, как оба вышеприведенных класса алгоритмов на любых входных данных.

Оказывается, существуют даже более быстрые алгоритмы. Это было показано в 1986 году в [10] Киркпатриком и Зейделем. Они описали весьма алгоритм с трудоемкостью O(n log h). Однако этот алгоритм имел чрезвычайно сложную логику.

Позже, в 1994 году, в [3, 15, 16] Тимоти Чен предложил чрезвычайно простой алгоритм с трудоемкостью O(n log h). Алгоритм Чена является оригинальным обобщение алгоритмов Джарвиса и Грэхема.

Предположим, что нам известно количество вершин выпуклой оболочки h. Тогда алгоритм Чена можно описать следующим образом:

Шаг 1. Произвольно разделим исходное множество S на подмножества S1, S 2 , ..., S n/h ,

каждое (кроме, возможно, последнего) размером h.

Шаг 2. Для каждого i, 1 i n / h , найдем выпуклую оболочку множества точек Si и обозначим её Ci.

Шаг 3. Теперь переходим к вычислению выпуклой оболочки всего множества S, используя методику «заворачивания подарка». Алгоритм начинает свою работу с нахождения точки p0 из S, которая гарантированно является вершиной выпуклой оболочки S. Обычно за точку p0 берется самая нижняя из точек множества S. Если S содержит несколько точек с минимальной ординатой, то берется самая левая из них. Очевидно, что точка p0 может быть найдена за время O(n). Пусть p1 – точка множества S, такая, что ребро p0 p1 является ребром выпуклой оболочки, направленным против часовой стрелки. Далее рассмотрим, каким образом следует находить p1.

Пусть l – луч, направленный из p0 в сторону положительного направления оси абсцисс. Заметим, что все точки множества S находятся левее этого луча или на нем. Для каждого i, 1 i n / h мы находим точку qi Si , которая является первой точкой, которой коснется

луч l, если его вращать против часовой стрелки. Далее, за линейное время мы можем найти точку qj из множества {q1, q2,..., q n/h } , для которой угол между l и p0qj минимален (если

таких точек несколько, то выбирается из них та, расстояние от которой до точки p0 максимально). Очевидно, что точка qj и является искомой точкой p1.

15

Рисунок 9. Алгоритм Чена

Найдя p1, мы таким же образом находим точку p2: пусть l' – луч, направленный из p0 и проходящий через p1. Как и ранее, все точки множества S находятся левее луча или на нем. Для каждого i, 1 i n / h мы выполняем «запрос на экстремальность», то есть находим

точку qi Si , которая является первой точкой, которой коснется луч l', если его вращать против часовой стрелки относительно точки p0. Тогда p2 – это та точка x из n / h точек qi ,

для которой угол между l' и p1x минимален. Мы продолжаем выполнять такие шаги «заворачивания подарка», пока не вернемся к исходной точке p0. Конец алгоритма.

Подсчитаем трудоемкость вышеприведенного алгоритма. Шаг 1 занимает время O(n). Выпуклая оболочка для каждого из исходных подмножеств Si может быть найдена за время

O( Si log Si ) = O(h log h) .

Таким образом, Шаг 2 в целом имеет трудоемкость

O(nh log h) =O(n log h) .

Рассмотрим Шаг 3. Начальная точка p0 может быть найдена за линейное время. Для нахождения p1 мы выпоняем «запрос на экстремальность» в каждом из подмножеств Si. Если осуществлять эти запросы с использованием метода бинарного поиска, описанного ниже в п.2.2.2, то каждый такой запрос будет иметь трудоемкость

O(log Si ) = O(log h) .

Таким образом, все запросы вместе займут время

O(nh log h) .

16

Это даст нам n / h точек-кандидатов на попадание в выпуклую оболочку. За время O(n / h) мы выбираем из них точку p1. Следовательно, имея p0, мы находим p1 алгоритмом с трудоемкостью O(n / h log h) .

Так как в выпуклой оболочке h вершин, то общая трудоемкость их нахождения такова:

O(n + h * nh log h) =O(n log h)

То есть, весь алгоритм нахождения выпуклой оболочки множества S имеет трудоемкость

O(n log h).

Однако заметим, что это выполняется только при условии априорной известности h. Как должен поступать алгоритм, если h не известно? Трюк состоит в том, что мы «подберем» необходимое значение h следующим специальным способом.

Предположим, что H – это текущее предполагаемое значение h. Мы запускаем вышеприведенный алгоритм, заменив h на H. Шаги 1 и 2 выполняются за время O(n log H). На шаге 3 может получиться две различных ситуации:

1. За H шагов заворачивания подарка мы вернулись в точку p0. Это может произойти, только если h H . В этом случае, выпуклая оболочка S с учетом трудоемкости третьего шага алгоритма может быть построена за время

O(n + h Hn log H ) = O(n log H )

2. После H шагов заворачивания подарка мы не достигли точки p0. Это может произойти, только если h > H, то есть наше предположение о количестве вершин выпуклой оболочки было слишком мало. В этом случае мы останавливаем алгоритм после H шагов. При этом мы затратили время O(n logH), но так и не нашли выпуклой оболочки множества S. Так как наше предположение о величине h было слишком мало, мы увеличиваем H и запускаем алгоритм снова.

Какие значения необходимо давать H на первом и последующих шагах приближения? Обычно начинают с H=4 или любого другого небольшого числа. Каждый раз, когда обнаруживается, что h<H, будем принимать H=H2

Пусть Hf – финальное значение H. То есть, запуская алгоритм в предположении, что количество вершин выпуклой оболочки равно Hf, мы успешно заканчиваем построение выпуклой оболочки не более чем за Hf шагов. Тогда мы знаем, что Hf h. Предыдущее предпола-

гаемое значение, которое было равно H f , оказалось слишком мало, то есть Hf < h2.

Теперь мы можем вычислить трудоемкость алгоритма в целом. Для каждого предполагаемого значения H мы тратим времени не больше c n log H. Следовательно, на финальном шаге мы тратим времени не больше c n log Hf. Трудоемкость предпоследнего шага не больше

c n log H f

=

1 c n log H f .

 

 

2

В общем, для i-го шага подбора H мы тратим времени не больше

c n log 2i H f

 

1

i

=

 

 

c n log H f .

2

 

 

 

 

Тогда общее время работы алгоритма равно

17

1 i c n log H f

i0 2

2c n log H f

2c n log h2

4c n log h

=O(n log h).

Полученная трудоемкость говорит о том, что цель – найти алгоритм, имеющий преимущества методов Грэхема и Джарвиса, достигнута. В среднем этот алгоритм имеет трудоемкость O(n log h). В худшем случае этот алгоритм будет иметь трудоемкость O(n log n).

Достоинства:

Алгоритм имеет трудоемкость O(n log h) в среднем, при этом в худщем случае он является оптимальным.

Недостатки:

Алгоритм не является открытым, т.е. необходимо априорное знание всего множества точек.

2.2 Особенности практической реализации алгоритмов построения выпуклой оболочки на плоскости

Все описания алгоритмов, приведенные выше, являются чисто теоретическими. При практической реализации конкретных алгоритмов возникает множество проблем, которые в теории не решаются и чаще всего даже и не возникают. Решение этих задач иногда позволяет существенно увеличить реальную скорость работы программы, реализующей тот или иной алгоритм.

2.2.1 Практические особенности сравнения углов

В большинстве алгоритмов построения выпуклой оболочки необходимо искать минимальные углы среди множества углов, заданных парами векторов. Однако очевидно, что выполнение тригононометрических операций существенно снижает скорость работы алгоритма. Ниже приведено описание известного способа избавления от тригонометрических операций [1].

Пусть a и b – две последовательные (против часовой стрелки) вершины выпуклой оболочки, а l – линия, проходящая через эти вершины. Будем считать, что эта линия является вектором, имеющим направление из a в b. Заметим, что все точки множества S находятся или левее l или на l.

Для каждой точки q S \ {b} пусть αq – угол между l и отрезком bq. Алгоритм Джарвиса ищет такую точку q, для которой этот угол является минимальным. Мы можем явно найти все углы αq, а потом найти минимальный из них. Рассмотрим способ нахождения этих углов. Во-первых, заметим, что αq – это угол между векторами b-a и q-b (см. рис. 4).

18

Рис. 10. Вычисление угла αq

Также заметим, что

(b a) (q b)= b a×q b×cosαq .

Следовательно,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(bx ax )(qx bx ) +(by ay )(qy by )

 

 

 

 

αq

 

 

 

 

 

 

 

= arccos

(b

 

a

 

)

2

+(b

 

a

 

)

2

(q

 

b

 

)

2

+(q

 

b

 

)

2

.

 

 

x

x

 

y

y

 

x

x

 

y

y

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Но сравнение этих углов в явном виде влечет большие вычислительные затраты. Опишем способ нахождения минимального угла αq без использования функций арккосинуса и вычисления квадратного корня. Для того, чтобы найти минимальный угол, не обязательно вычислять углы, достаточно их сравнивать. То есть, имея две точки q и r, нам достаточно определить, αq <αr , αq >αr , или αq =αr . Это можно сделать, не вычисляя точных значений уг-

лов. Рассмотрим следующий определитель:

 

 

 

 

bx

by

1

 

(qx bx ) (qy by )

 

 

 

 

 

 

 

(b, q, r) =

qx

qy

1

=

(1)

(rx bx )

(ry by )

 

rx

ry

1

 

 

 

 

 

 

 

Из аналитической геометрии мы знаем, что его значение равно удвоенной ориентированной площади треугольника bqr, знак которого положителен, если αq <αr , отрицателен, если

αq >αr , и равен нулю, если αq =αr (рис. 10). Таким образом, мы можем сравнивать углы, вычисляя знак выражения

(b, q, r) = (qx bx )(ry by ) (qy by )(rx bx ) .

(2)

Это займет всего пять вычитаний и два умножения, что намного проще, чем вычисление арккосинусов и квадратных корней.

Если существует более одной точки, имеющей минимальный угол α, необходимо выбрать ту из них, расстояние от которой до точки b максимально. Конечно, мы для каждой такой точки q с минимальным углом α мы можем вычислить расстояние

d(b, q) = (bx qx )2 +(by qy )2 ,

(3)

19

 

а потом выбрать ту точку, для которой

Рис. 11. - удвоенная ориентированная площадь треугольника bqr

d(b,q) минимально. На самом деле вычислять расстояние не обязательно – достаточно всего лишь сравнить расстояния d(b,q) и d(b, r) . Для того, чтобы не вычислять квадратных корней, можно использовать следующее соотношение:

d (b, q) < d (b, r) (bx qx )2 + (by qy )2 < (bx rx )2 +(by ry )2 .

Для ускорения практической работы алгоритма это делается с использованием формул

(1) и (2). Если углы совпадают, то точки сортируются относительно расстояния от центра координат (с использованием формулы (3)).

Во многих алгоритмах используется следующая примитивная операция: пусть x, y и z – три несовпадающие точки. Тогда считается, что (x, y, z) является правым поворотом, если точка z находится справа от отрезка, направленного из x в y. Если z находится слева от отрезка, направленного из x в y, то (x, y, z) является левым поворотом.

Из формул (1) и (2) следует, что

 

 

(x, y, z) является правым поворотом,

если(x, y, z) < 0;

(4)

левым поворотом,

если(x, y, z) > 0.

 

Если (x, y, z) = 0 , то точки x, y, z считаются коллинеарными.

2.2.2 Иерархическое представление выпуклых оболочек. Практические особенности реализации алгоритма Чена.

В алгоритме Чена предполагается, что выполнение «запроса на экстремальность» имеет логарифмическую трудоемкость. Эта трудоемкость может быть достигнута, только с использованием специализированной структуры данных. Структура, удовлетворяющая вышеописанным требованиям, была описана Чазелем и Добкиным в [11].

Рассмотрим следующую проблему: имеется множество S, состоящее из n точек. Необходимо хранить это множество в такой структуре данных, которая позволяет отвечать на «запросы на экстремальность».

Смысл такого запроса заключается в следующем. Пусть дана некая точка q на плоскости и луч l, направленный из q вправо. Все точки множества S лежат или на луче l или слева от него. Ответом на запрос будет являться точка, которую l коснется первой, если его вращать вокруг точки q против часовой стрелки (рис. 12).

20

Соседние файлы в папке Выпуклые оболочки