Статические выпуклые оболочки: Джарвис, Грэхем, Эндрю, Чен, QuickHull — различия между версиями
Yurik (обсуждение | вклад) (→Псевдокод) |
Yurik (обсуждение | вклад) (→Алгоритм) |
||
| Строка 101: | Строка 101: | ||
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D1%8D%D1%85%D0%B5%D0%BC%D0%B0 Русская статья — Wikipedia] | * [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D1%8D%D1%85%D0%B5%D0%BC%D0%B0 Русская статья — Wikipedia] | ||
| − | = Алгоритм = | + | = Алгоритм Эндрю = |
| + | |||
| + | Алгоритм, очень похожий на алгоритм Грехема. | ||
== Описание Алгоритма == | == Описание Алгоритма == | ||
| + | [[File:andrew1.png|thumb|400px|Промежуточный шаг алгоритма. Зелеными линиями обозначена текущая выпуклая оболочка, синими - промежуточные соединения точек, красными - те отрезки, которые раньше входили в оболочку, а сейчас нет. На текущем шаге при добавлении точки <tex>p</tex> последовательно убираем из оболочки точки с <tex>i+3</tex>-ей до <tex>i+1</tex>-ой]] | ||
| + | 1)Сортируем все точки по х-координате. | ||
| + | |||
| + | 2)Пусть самая правая точка - <tex>p_1</tex>. Добавляем ее в ответ. | ||
| + | |||
| + | 3)Идем от <tex>p_1</tex> по уменьшению х-координаты. Берем следующую по счету точку t. Пока t и две последних точки в текущей оболочке p_i и p_{i-1} образуют неправый поворот, удаляем из оболочки p_i. (если в оболочке одна точка <tex>p_1</tex>, считаем, что перед ней точка <tex>(0, -inf)</tex> ) | ||
| + | |||
| + | 4)Добавляем в ответ <tex>t</tex>. | ||
| + | |||
| + | 5)Делаем так, пока не дойдем до <tex>p_0</tex> - самой левой точки. | ||
| + | |||
| + | 6)Повторим проход 3-5 для "нижней" половины оболочки в порядке увеличения х-координаты. | ||
| + | |||
| + | <br/> | ||
== Корректность == | == Корректность == | ||
| + | |||
| + | <br/> | ||
| + | См. доказательство алгоритма Грехема. | ||
| + | <br/> | ||
== Псевдокод == | == Псевдокод == | ||
| + | |||
| + | Inplace-реализация алгоритма. <tex>S[1..n]</tex> - исходное множество, <tex>n > 2</tex> | ||
| + | |||
| + | Andrew(S) | ||
| + | sort S[1..n] by x-coordinate backward(than by y backward) | ||
| + | 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]) | ||
| + | k++ | ||
| + | sort S[k + 1..n] by x-coordinate (than by y) | ||
| + | for p = k + 1..n | ||
| + | while S[k - 1], S[k], S[p] has non-right orientation | ||
| + | k-- | ||
| + | swap(S[p], S[k + 1]) | ||
| + | return k + 1 | ||
== Сложность == | == Сложность == | ||
| + | |||
| + | Сортировка точек занимает <tex>O(n \log n)</tex> времени. При обходе каждая точка добавляется в ответ не более одного раза, поэтому сложность двух обходов - <tex> 2 \cdot O(n)</tex>. Суммарное время - <tex>O(n \log n)</tex>. | ||
== Ссылки == | == Ссылки == | ||
| − | * [http://en.wikipedia.org/wiki/ | + | * [http://en.wikipedia.org/wiki/Graham_scan#Notes Одно предложение о нем] |
| − | |||
= Алгоритм Чена = | = Алгоритм Чена = | ||
Версия 18:08, 16 января 2014
| Конспект не готов. |
Ниже приводятся основные алгоритмы построения выпуклых оболочек статического множества. Используются обозначения: - размер входных данных, - размер оболочки.
Содержание
Алгоритм Джарвиса
По-другому "Gift wrapping algorithm" (Заворачивание подарка).
Описание Алгоритма
1) Возьмем самую правую нижнюю точку нашего множества. Добавляем ее в ответ.
2) На каждом следующем шаге для последнего добавленного ищем среди всех недобавленных точек и с максимальным полярным углом относительно (Если углы равны, надо сравнивать по расстоянию). Добавляем в ответ. Если , заканчиваем алгоритм.
Корректность
Точка , очевидно, принадлежит оболочке. На каждом последующем шаге алгоритма мы получаем прямую , по построению которой все точки множества лежат слева от нее. Значит, выпуклая оболочка состоит из -ых и только из них.
Псевдокод
Inplace-реализация алгоритма. - исходное множество,
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
Сложность
Добавление каждой точки в ответ занимает времени, всего точек будет , поэтому итоговая сложность .
Ссылки
Алгоритм Грэхема
Описание Алгоритма
1)Находим самую правую нижнюю точку множества , добавляем в ответ.
2)Сортируем все остальные точки по полярному углу относительно .
3)Добавляем в ответ - самую первую из отсортированных точек.
4)Берем следующую по счету точку . Пока и две последних точки в текущей оболочке и образуют неправый поворот, удаляем из оболочки .
5)Добавляем в оболочку .
6)Делаем п.5, пока не закончатся точки.
Корректность
Докажем, что на каждом шаге множество -тых является выпуклой оболочкой всех уже рассмотренных точек. Доказательство проведем по индукции.
1)База. Для трех первых точек утверждение, очевидно, выполняется.
2)Переход. Пусть для точек оболочки совпадают. Докажем, что и для точек они совпадут.
Рассмотрим истинную оболочку , где - множество всех точек из , видимых из . Так как мы добавляли точки в нашу оболочку против часовой стрелки и так как -тая точка лежит в , то состоит из нескольких подряд идущих последних добавленных в оболочку точек, и именно их мы удаляем на текущем шаге. Поэтому наша оболочка и истинная для точек совпадают.
Тогда по индукции оболочки совпадают и для .
Псевдокод
Inplace-реализация алгоритма. - исходное множество,
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 + 1
Сложность
Сортировка точек занимает времени. При обходе каждая точка добавляется в ответ не более одного раза, поэтому сложность этой части - . Суммарное время - .
Ссылки
Алгоритм Эндрю
Алгоритм, очень похожий на алгоритм Грехема.
Описание Алгоритма
1)Сортируем все точки по х-координате.
2)Пусть самая правая точка - . Добавляем ее в ответ.
3)Идем от по уменьшению х-координаты. Берем следующую по счету точку t. Пока t и две последних точки в текущей оболочке p_i и p_{i-1} образуют неправый поворот, удаляем из оболочки p_i. (если в оболочке одна точка , считаем, что перед ней точка )
4)Добавляем в ответ .
5)Делаем так, пока не дойдем до - самой левой точки.
6)Повторим проход 3-5 для "нижней" половины оболочки в порядке увеличения х-координаты.
Корректность
См. доказательство алгоритма Грехема.
Псевдокод
Inplace-реализация алгоритма. - исходное множество,
Andrew(S)
sort S[1..n] by x-coordinate backward(than by y backward)
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])
k++
sort S[k + 1..n] by x-coordinate (than by y)
for p = k + 1..n
while S[k - 1], S[k], S[p] has non-right orientation
k--
swap(S[p], S[k + 1])
return k + 1
Сложность
Сортировка точек занимает времени. При обходе каждая точка добавляется в ответ не более одного раза, поэтому сложность двух обходов - . Суммарное время - .
Ссылки
Алгоритм Чена
Алгоритм QuickHull
Описание Алгоритма
1)Найдем самую левую нижнюю точку и самую правую нижнюю точку .
2)Возьмем все точки выше прямой .
3)Найдем среди этого множества точку , наиболее отдаленную от прямой (если таких несколько, взять самую правую).
4)Рекурсивно повторить шаги 2-3 для прямых и , пока есть точки.
5)Добавить в ответ точки , полученные в п. 3.
6)Повторить пункты 2-5 для (то есть для "нижней" половины).
7)Ответ - объединение списков из п. 5 для верхней и нижней половины.
Корректность
Очевидно, что выпуклая оболочка всего множества является объединением выпуклых оболочек для верхнего и нижнего множества. Докажем, что алгоритм верно строит оболочку для верхнего множества, для правого рассуждения аналогичны. Точки и принадлежат оболочке.
- Пусть какая-то точка входит в нашу оболочку, но не должна.
Назовем эту точку . По алгоритму эта точка появилась как самая удаленная от некой прямой . Так как не входит в оболочку, то существует прямая из настоящей выпуклой оболочки, что лежит снизу от прямой. Тогда какая-то из и удалена от прямой дальше , что противоречит алгоритму.
- Наоборот, пусть какой-то точки в нашей оболочке нет, а должна быть.
Пойдем вниз рекурсии в те ветки, где есть . В какой-то момент окажется внутри некоторого треугольника. Но тогда возникает противоречие с тем, что принадлежит выпуклой оболочке - противоречие.
Таким образом, наша оболочка совпадает с истинной, а значит алгоритм корректен.
Реализация
Заметим, что длина высоты, опущенная из точки на отрезок , пропорциональна векторному произведению , поэтому для сравнения можно использовать именно это.
Псевдокод
Inplace-реализация алгоритма. - исходное множество. - рекурсивная функция, находящая оболочку подмножества . В реализации в конце каждого подмножества находятся эл-ты, точно не принадлежащие оболочке.
QuickHull(S) find i such that S[i] has the highest x-coordinate and lowest y-coordinate swap(S[1], S[i]) find i such that S[i] has the lowest x-coordinate and lowest y-coordinate swap(S[n], S[i]) k = partition1(S) // разбиваем на те эл-ты, которые лежат над прямой и на остальные a = quick_hull(S, 1, k) b = quick_hull(S, k + 1, n); swap(S[a..k], S[k + 1, b]) return start + (a - 1) + (b - k - 1)
quick_hull(S, start, end) find i such that S[i], S[start], S[end] has maximum value (a, b) = partition2(S, start, end, S[i]) //свапаем эл-ты S так, чтобы сначала были все эл-ты над прямой S[start]S[i], потом S[i]S[end], потом все остальное c = quick_hull(S, start, a) d = quick_hull(S, a + 1, b) swap(S[c..a], S[a + 1..d]) return start + (a - c) + (d - b)
Сложность
Пусть время, необходимое для нахождения оболочки над некой прямой и множеством точек есть Тогда , где - множества над полученными прямыми. Отсюда видно, что в худшем случае, алгоритм тратит . На рандомных же данных это число равно