Алгоритм Чана: как строить выпуклую оболочку за O(n log h)

Алгоритм Чана (Chan's algorithm, опубликован Тимоти Чаном в 1996 году) строит выпуклую оболочку точек на плоскости за время , где - число вершин самой оболочки. Это output-sensitive результат: чем меньше оболочка, тем быстрее работает алгоритм, причём асимптотика оптимальна по нижней оценке для output-sensitive класса. Идея красивая: взять Graham scan, который силён на , и Jarvis march, который силён на , и комбинировать их так, чтобы каждый компенсировал слабость другого. Параметр - гипотетический «угаданный» размер оболочки - подбирается через геометрическую прогрессию с early termination.
Мотивация: чем плохи Graham и Jarvis по отдельности
Классические алгоритмы выпуклой оболочки берут крайние точки множества из точек на плоскости. Graham scan (1972) сортирует точки по полярному углу относительно нижней опорной и обходит их стеком с проверкой поворота - итого . Эта оценка оптимальна в худшем случае: задача сводится к сортировке, и - нижняя оценка по модели сравнений.
Jarvis march (gift wrapping, 1973) идёт иначе: начиная с граничной точки, шаг за шагом ищет следующую вершину оболочки как «самую правую» из оставшихся. Каждый шаг - , всего шагов, итого . Если оболочка маленькая (), Jarvis быстрее Грэхема. Если оболочка огромная (, например, точки на окружности), Jarvis деградирует до .
Возникает вопрос: можно ли получить алгоритм, который всегда не хуже обоих - то есть ? Output-sensitive нижняя оценка известна давно. Первый match - Kirkpatrick–Seidel «ultimate planar convex hull» (1986) - достигает , но через сложный bridge-finding с медианой и prune-and-search. Чан в 1996 нашёл намного более простую конструкцию из двух стандартных кубиков.
Идея: Graham внутри, Jarvis снаружи
Пусть нам как-то заранее «угадан» размер оболочки . Алгоритм Чана делает следующее.
Шаг 1. Разбиение и локальные оболочки. Разбить точек на групп по точек. В каждой группе построить локальную выпуклую оболочку обычным Graham scan за . Суммарно по всем группам - .
Шаг 2. Jarvis march по группам. Запустить Jarvis на «макроуровне»: на каждом шаге надо найти следующую вершину глобальной оболочки. Делается это так - для каждой из локальных оболочек найти касательную из текущей вершины к этой оболочке. Касательная к выпуклому многоугольнику размером ищется бинарным поиском за . Среди кандидатов выбрать «самого правого» - сравнений. Один шаг Jarvis - . Всего шагов - , итого .
Сложение. . При получаем . То есть если бы мы знали , всё бы сошлось.
Угадывание через геометрическую прогрессию
Проблема - мы не знаем заранее: оно и есть то, что мы хотим вычислить. Решение Чана - пробовать по геометрической прогрессии (или проще - с двойным возведением) и запускать алгоритм с early termination: если после шагов Jarvis обход не замкнулся, значит, , прерываем и пробуем следующее .
Точнее, на итерации выставляем , выполняем шаги 1 и 2, но ограничиваем Jarvis-цикл итерациями. Если оболочка построилась (вернулись в стартовую вершину за шагов) - выводим её и стоп. Если нет - увеличиваем и повторяем.
Затраты по итерациям складываются в геометрический ряд:
Последняя итерация даёт , и алгоритм гарантированно завершается. Сумма доминируется последним членом - это и есть итоговое .
Корректность
Локальные оболочки шага 1 содержат все граничные точки своих групп (Graham scan корректен). Любая вершина глобальной оболочки - граничная точка своей группы (если бы была внутренней, нашлась бы группа-сосед с точкой левее/правее, противоречие). Значит, каждая вершина глобальной оболочки присутствует хотя бы в одной локальной.
Касательная из точки к выпуклому многоугольнику размером корректно находится бинарным поиском по углам. Шаг Jarvis выбирает «самую правую» точку среди всех касательных - это и есть следующая глобальная вершина. Обход замыкается за шагов, потому что глобальная оболочка имеет ровно вершин.
Early termination не теряет корректности: если оболочка не замкнулась за шагов, мы выкидываем промежуточные результаты и перезапускаем с большим . Не запускается ни одной лишней операции на корректном выходе.
Итоговая сложность
Соберём:
- Шаг 1 на итерации - .
- Шаг 2 на итерации - (потому что мы ограничили Jarvis шагами).
- Итерации .
Каждая итерация - . Сумма геометрическая, доминируется последним членом, который . Итог: .
Память - : хранятся точки, локальные оболочки, текущая глобальная вершина. Никаких дополнительных структур.
Сравнение с Kirkpatrick–Seidel
Оба алгоритма достигают и оба оптимальны output-sensitive. Различия:
- Конструкция. Чан - два знакомых кубика (Graham + Jarvis) и геометрическая прогрессия. Kirkpatrick–Seidel - bridge-finding с prune-and-search на медиане, -fold marriage-before-conquest. Реализация Чана умещается в 80 строк C++, K-S - 200+.
- Константа. У Чана константа выше в Jarvis-шаге (бинарный поиск касательной для каждой группы), но проще структура памяти. На практике в библиотеках (CGAL, Boost.Geometry) чаще берут Чана или Andrew's monotone chain - K-S редко.
- 3D-обобщение. Чан обобщается до в 3D (Chan 1996 же), K-S - нет.
Поэтому Чан - фактический рабочий «output-sensitive ultimate» алгоритм.
Когда брать алгоритм Чана
- Огромные облака точек с малой оболочкой. GPS-треки, LiDAR-сканы, выборки из равномерного распределения на квадрате (там в среднем). Чан даёт , Graham - .
- Стримы сенсоров. Когда велико, а граница мала и заранее неизвестна - Чан адаптируется.
- 3D convex hull в варианте Chan 1996 - в 3D, единственный простой output-sensitive.
Не брать, если (точки на окружности) - там Graham/Andrew проще и константа меньше. Также не брать, если важна устойчивость к коллинеарным случаям без специальной обработки - Чан наследует все подводные камни и Graham, и Jarvis сразу.
Типовые задачи
- Минимальный охватывающий многоугольник. Когда нужна только сама оболочка для дальнейших операций (диаметр, площадь, проверка вхождения).
- Broad-phase коллизии в физике. Convex hull mesh как грубое приближение формы.
- GIS-аналитика. Ареал наблюдений по тысячам/миллионам отметок.
- Кластеризация в ML. Convex hull кластера для визуализации и проверки разделимости.
- Олимпиадные задачи. При и заранее малом Чан проходит, Graham - нет.
Частые ошибки
- Берут вместо . Тогда число итераций - , и сумма - на хуже оптимума.
- Не ограничивают Jarvis шагами. Без early termination первая же итерация с просто зациклится или построит мусор; алгоритм перестаёт быть output-sensitive.
- Используют линейный поиск касательной вместо бинарного. Касательная к выпуклому многоугольнику размером ищется за - это критично для асимптотики.
- Не сжимают дубликаты и не обрабатывают коллинеарные точки. Те же баги, что в Graham и Jarvis, унаследованы целиком.
- Считают с плавающей точкой при больших координатах. Целочисленный обязателен, иначе знак поворота врёт.
FAQ
Чем Chan's algorithm лучше Kirkpatrick–Seidel при одинаковой ? Реализация. Чан - это пять-семь страниц учебника с двумя стандартными кубиками. K-S - bridge-finding на медиане, prune-and-search, -fold conquer; в учебниках занимает раздел на 20 страниц. На практике Чан реализован в CGAL и широко используется; K-S остаётся в основном теоретическим результатом. Асимптотика одинаковая, константа у Чана чуть выше, но кодоёмкость в разы меньше.
Можно ли построить трёхмерную выпуклую оболочку Чаном? Да. В той же работе 1996 года Чан показал для 3D через тот же приём - разбить на группы, локально построить или оболочкой, обходить «снаружи» с касательными плоскостями. В 3D output-sensitive нижняя оценка тоже , и Чан её достигает.
Что выбрать на собеседовании, если просят convex hull? Сначала уточните: «Знаем ли мы что-то про ?». Если нет - Andrew's monotone chain (, проще Graham и стабильнее численно). Если ожидается малая оболочка на больших данных - Chan (). На полу-устной задаче можно обойтись Graham - все его знают наизусть. Чана упоминают, если интервьюер хочет услышать про output-sensitive.
Коротко
Алгоритм Чана (1996) - output-sensitive алгоритм выпуклой оболочки на плоскости со сложностью , где - число вершин оболочки. Идея: разбить точек на группы по и в каждой построить локальную оболочку Graham scan за ; затем Jarvis march по группам, на каждом шаге ища касательную бинарным поиском за . При суммарно - . Значение заранее неизвестно, поэтому перебирают по двойной геометрической прогрессии с early termination. Альтернатива той же асимптотики - Kirkpatrick–Seidel, но он значительно сложнее в реализации. Чана используют на больших облаках точек с малой оболочкой, в 3D-обобщении и в библиотеках вычислительной геометрии.
Читайте также

Алгоритм Грэхема: строим выпуклую оболочку точек
Алгоритм Грэхема строит выпуклую оболочку набора точек за O(n log n): опорная точка, сортировка по углу и обход стеком. Разбираем шаги и проверку поворота.

Алгоритм Рабина-Карпа: поиск подстроки за O(n+m)
Разбираем алгоритм Рабина-Карпа: как полиномиальный хеш и скользящее окно ускоряют поиск подстроки до O(n+m) в среднем, почему бывают ложные совпадения и при чём тут плагиат.

Распределение Фишера критические значения: как искать F-квантили
Распределение Фишера и его критические значения: что такое F-распределение, как читать таблицу критических значений по двум степеням свободы, как применять F-квантили в F-тесте на равенство дисперсий и в дисперсионном анализе.