Статические выпуклые оболочки: Джарвис, Грэхем, Эндрю, Чен, QuickHull — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Псевдокод)
(Алгоритм QuickHull)
Строка 121: Строка 121:
 
= Алгоритм Чена =
 
= Алгоритм Чена =
 
= Алгоритм QuickHull =
 
= Алгоритм QuickHull =
 +
 +
== Описание Алгоритма ==
 +
[[File:hull.png|thumb|250px|Промежуточный шаг алгоритма. Для прямой <tex>p_i p_1</tex> нашли точку <tex>p</tex>. Над прямыми <tex>p_i p</tex> и <tex>p p_1</tex> точек нет, поэтому переходим к следующей прямой <tex>p_0 p_i</tex>.]]
 +
 +
1)Найдем самую левую нижнюю точку <tex>p_0</tex> и самую правую нижнюю точку <tex>p_1</tex>.
 +
 +
2)Возьмем все точки выше прямой <tex>p_0 p_1</tex>.
 +
 +
3)Найдем среди этого множества точку <tex>p_i</tex>, наиболее отдаленную от прямой (если таких несколько, взять самую правую).
 +
 +
4)Рекурсивно повторить шаги 2-3 для прямых <tex>p_0 p_i</tex> и <tex>p_i p_1</tex>, пока есть точки.
 +
 +
5)Добавить в ответ точки <tex>p_0 .. p_i .. p_1</tex>, полученные в п. 3.
 +
 +
6)Повторить пункты 2-5 для <tex>p_1 p_0</tex> (то есть для "нижней" половины).
 +
 +
7)Ответ - объединение списков из п. 5 для верхней и нижней половины.
 +
 +
<br/><br/>
 +
 +
== Корректность ==
 +
 +
[[File:hull1.png|thumb|200px|]]
 +
 +
Очевидно, что выпуклая оболочка всего множества является объединением выпуклых оболочек для верхнего и нижнего множества. Докажем, что алгоритм верно строит оболочку для верхнего множества, для правого рассуждения аналогичны.
 +
Точки <tex>p_0</tex> и <tex>p_1</tex> принадлежат оболочке.
 +
 +
* Пусть какая-то точка входит в нашу оболочку, но не должна.
 +
 +
Назовем эту точку <tex>t</tex>. По алгоритму эта точка появилась как самая удаленная от некой прямой <tex>t_1 t_2</tex>. Так как <tex>t</tex> не входит в оболочку, то существует прямая <tex>t_3 t_4</tex> из настоящей выпуклой оболочки, что <tex>t</tex> лежит снизу от прямой. Тогда какая-то из <tex>t_3</tex> и <tex>t_4</tex> удалена от прямой дальше <tex>t</tex>, что противоречит алгоритму.
 +
 +
* Наоборот, пусть какой-то точки <tex>t</tex> в нашей оболочке нет, а должна быть.
 +
 +
Пойдем вниз рекурсии в те ветки, где есть <tex>t</tex>. В какой-то момент <tex>t</tex> окажется внутри некоторого треугольника. Но тогда возникает противоречие с тем, что <tex>t</tex> принадлежит выпуклой оболочке - противоречие.
 +
 +
Таким образом, наша оболочка совпадает с истинной, а значит алгоритм корректен.
 +
 +
== Псевдокод ==
 +
 +
 +
 +
== Сложность ==
 +
 +
Пусть время, необходимое для нахождения оболочки над некой прямой и множеством точек <tex>S</tex> есть <tex>T(S)</tex>
 +
Тогда <tex>T(S) = O(\|S\|) + T(A \in S) + T(B \in S)</tex>, где <tex>A, B</tex> - множества над полученными прямыми. Отсюда видно, что в худшем случае, алгоритм тратит <tex>O(n^2)</tex>. На рандомных же данных это число равно <tex>O(n \log n)</tex>
 +
 +
== Ссылки ==
 +
 +
* [http://en.wikipedia.org/wiki/QuickHull Английская статья — Wikipedia]

Версия 16:06, 16 января 2014

Конспект не готов.

Ниже приводятся основные алгоритмы построения выпуклых оболочек статического множества. Используются обозначения: [math]n[/math] - размер входных данных, [math]k[/math] - размер оболочки.

Алгоритм Джарвиса

По-другому "Gift wrapping algorithm" (Заворачивание подарка).

Описание Алгоритма

Промежуточный шаг алгоритма. Для точки [math]p_i[/math] ищем следующую перебором.


1) Возьмем самую правую нижнюю точку [math]p_0[/math] нашего множества. Добавляем ее в ответ.

2) На каждом следующем шаге для последнего добавленного [math]p_i[/math] ищем [math]p_{i + 1}[/math] среди всех недобавленных точек и [math]p_0[/math] с максимальным полярным углом относительно [math]p_i[/math] (Если углы равны, надо сравнивать по расстоянию). Добавляем [math]p_{i + 1}[/math] в ответ. Если [math]p_{i + 1} == p_0[/math] , заканчиваем алгоритм.






Корректность

Точка [math]p_0[/math], очевидно, принадлежит оболочке. На каждом последующем шаге алгоритма мы получаем прямую [math]p_{i-1}p_i[/math], по построению которой все точки множества лежат слева от нее. Значит, выпуклая оболочка состоит из [math]p_{i}[/math]-ых и только из них.

Псевдокод

Inplace-реализация алгоритма. [math]S[1..n][/math] - исходное множество, [math]n \gt 2[/math]

 Jarvis(S)
   find i such that S[i] has the lowest y-coordinate and highest x-coordinate
   p0 = S[i]
   pi = p0
   k = 0
   do 
     k++
     for i = k..n 
       if S[i] has lower angle and higher distance than S[k] in relation to pi
         swap(S[i], S[k])
     pi = S[k]
   while pi != p0
   return k

Сложность

Добавление каждой точки в ответ занимает [math]O(n)[/math] времени, всего точек будет [math]k[/math], поэтому итоговая сложность [math]O(nk)[/math].

Ссылки

Алгоритм Грэхема

Описание Алгоритма

Промежуточный шаг алгоритма. Зелеными линиями обозначена текущая выпуклая оболочка, синими - промежуточные соединения точек, красными - те отрезки, которые раньше входили в оболочку, а сейчас нет. На текущем шаге при добавлении точки [math]p[/math] последовательно убираем из оболочки точки с [math]i+3[/math]-ей до [math]i+1[/math]-ой

1)Находим самую правую нижнюю точку множества [math]p_0[/math], добавляем в ответ.

2)Сортируем все остальные точки по полярному углу относительно [math]p_0[/math].

3)Добавляем в ответ [math]p_1[/math] - самую первую из отсортированных точек.

4)Берем следующую по счету точку [math]t[/math]. Пока [math]t[/math] и две последних точки в текущей оболочке [math]p_i[/math] и [math]p_{i-1}[/math] образуют неправый поворот, удаляем из оболочки [math]p_i[/math].

5)Добавляем в оболочку [math]t[/math].

6)Делаем п.5, пока не закончатся точки.

Корректность

Конспект не готов.

Докажем, что на каждом шаге множество [math]p_i[/math]-тых является выпуклой оболочкой всех уже рассмотренных точек. Доказательство проведем по индукции.

1)База. Для трех первых точек утверждение, очевидно, выполняется.

2)Переход. Предположим от противного, что утверждение на [math]i[/math]-м шаге перестает выполняться. Тогда существует два варианта:

  • В нашей оболочке есть лишние точки.
Этого не может быть, так как на каждом шаге мы добавляем ровно одну точку, которая точно входит в оболочку, так как имеет наибольший полярный угол среди всех остальных.
  • В нашей оболочке нет каких-то точек, которые есть в настоящей.
Удаляем мы только те 

Псевдокод

Inplace-реализация алгоритма. [math]S[1..n][/math] - исходное множество, [math]n \gt 2[/math]

Graham(S)
  find i such that S[i] has the lowest y-coordinate and highest x-coordinate
  swap(S[i], S[1])
  sort S[2..n] by angle in relation to S[1]
  k = 2
  for p = 3..n
    while S[k - 1], S[k], S[p] has non-right orientation
      k--
    swap(S[p], S[k + 1])   
  return k

Сложность

Сортировка точек занимает [math]O(n \log n)[/math] времени. При обходе каждая точка добавляется в ответ не более одного раза, поэтому сложность этой части - [math]O(n)[/math]. Суммарное время - [math]O(n \log n)[/math].

Ссылки

Алгоритм

Описание Алгоритма

Корректность

Псевдокод

Сложность

Ссылки

Алгоритм Чена

Алгоритм QuickHull

Описание Алгоритма

Промежуточный шаг алгоритма. Для прямой [math]p_i p_1[/math] нашли точку [math]p[/math]. Над прямыми [math]p_i p[/math] и [math]p p_1[/math] точек нет, поэтому переходим к следующей прямой [math]p_0 p_i[/math].

1)Найдем самую левую нижнюю точку [math]p_0[/math] и самую правую нижнюю точку [math]p_1[/math].

2)Возьмем все точки выше прямой [math]p_0 p_1[/math].

3)Найдем среди этого множества точку [math]p_i[/math], наиболее отдаленную от прямой (если таких несколько, взять самую правую).

4)Рекурсивно повторить шаги 2-3 для прямых [math]p_0 p_i[/math] и [math]p_i p_1[/math], пока есть точки.

5)Добавить в ответ точки [math]p_0 .. p_i .. p_1[/math], полученные в п. 3.

6)Повторить пункты 2-5 для [math]p_1 p_0[/math] (то есть для "нижней" половины).

7)Ответ - объединение списков из п. 5 для верхней и нижней половины.



Корректность

Hull1.png

Очевидно, что выпуклая оболочка всего множества является объединением выпуклых оболочек для верхнего и нижнего множества. Докажем, что алгоритм верно строит оболочку для верхнего множества, для правого рассуждения аналогичны. Точки [math]p_0[/math] и [math]p_1[/math] принадлежат оболочке.

  • Пусть какая-то точка входит в нашу оболочку, но не должна.

Назовем эту точку [math]t[/math]. По алгоритму эта точка появилась как самая удаленная от некой прямой [math]t_1 t_2[/math]. Так как [math]t[/math] не входит в оболочку, то существует прямая [math]t_3 t_4[/math] из настоящей выпуклой оболочки, что [math]t[/math] лежит снизу от прямой. Тогда какая-то из [math]t_3[/math] и [math]t_4[/math] удалена от прямой дальше [math]t[/math], что противоречит алгоритму.

  • Наоборот, пусть какой-то точки [math]t[/math] в нашей оболочке нет, а должна быть.

Пойдем вниз рекурсии в те ветки, где есть [math]t[/math]. В какой-то момент [math]t[/math] окажется внутри некоторого треугольника. Но тогда возникает противоречие с тем, что [math]t[/math] принадлежит выпуклой оболочке - противоречие.

Таким образом, наша оболочка совпадает с истинной, а значит алгоритм корректен.

Псевдокод

Сложность

Пусть время, необходимое для нахождения оболочки над некой прямой и множеством точек [math]S[/math] есть [math]T(S)[/math] Тогда [math]T(S) = O(\|S\|) + T(A \in S) + T(B \in S)[/math], где [math]A, B[/math] - множества над полученными прямыми. Отсюда видно, что в худшем случае, алгоритм тратит [math]O(n^2)[/math]. На рандомных же данных это число равно [math]O(n \log n)[/math]

Ссылки