Flip-алгоритм для нахождения триангуляции Делоне/диаграммы Вороного

Правка ru16, от VitaliiV, 2025-12-02 19:31:39

Всем привет! Недавно я захотел сдать задачу 102482G - Panda Preserve, и я не смог найти нигде простую реализацию построения триангуляции Делоне.

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

Начнём с нескольких определений и фактов:

Триангуляция — это разбиение некоторого множества точек S на треугольники, при этом внутренние области треугольников не пересекаются.

Триангуляция Делоне — это такое разбиение некоторого множества точек S на треугольники, что внутри окружности каждого треугольника не находится никаких точек из множества.

Диаграмма Вороного — такое разбиение плоскости, при котором каждая область этого разбиения образует множество точек, более близких к одному из элементов множества S, чем к любому другому элементу множества.

Диаграмма Вороного и триангуляция Делоне являются двойственными графами

Алгоритм построения триангуляции Делоне за $$$O(n^2)$$$

Прежде всего, стоит сказать, что триангуляция Делоне может быть построена значительно быстрее чем за $$$O(n^2)$$$. Подробнее об этом можно прочитать тут. Однако алгоритм фортуна, приведённый тут, слишком сложный для понимания, и написать его за время контеста крайне тяжело.

Реализация за $$$O(n^2)$$$ довольно простая в плане идеи и состоит из 2 шагов.

Шаг 1

Построим произвольную триангуляцию заданного множества точек. Это построение похоже на построение выпуклой оболочки. Среди всех точек множества найдём точку с наименьшей x-координатой, а если таких несколько, то с наименьшей y-координатой. Пусть это будет какая-то точка P. Сдвинем все координаты так, чтобы точка p оказалась в начале координат. Вычисляем полярные углы всех точек относительно точки p для сортировки вокруг нее. Далее будем строить триангуляцию в 2 этапа:

На первом этапе соединим все точки с точкой p и соседние точки между собой. Если бы гарантировалось что исходный набор точек образуют выпуклый многоугольник, то этого было бы уже достаточно, однако это не гарантируется.

На втором этапе будем действовать аналогично алгоритму Грэхема для построения выпуклой оболочки. Каждый раз при добавлении новой точки, необходимо проверить не нарушается ли условие выпуклости. Если нарушается, то мы нашли "впадину", которую нужно заполнить. ![](https://i.postimg.cc/QC3YZ0gB/ris1.png)На рисунке, при попытке добавить вершину 3 в выпуклую оболочку, обнаружилось что условие выпуклости нарушается, и треугольник 123 надо добавить в триангуляцию

Шаг 2

У нас уже есть какая-то триангуляция набора точек, однако она пока что не удовлетворяет критериям триангуляции Делоне. Будем итеративно улучшать нашу триангуляцию. На каждом шаге будем искать четырехугольник ABCD такой, что в нем есть ребро AC и треугольники ABC и ACD не удовлетворяют критерию Делоне. Операцией flip будем называть удаления ребра AC и добавления ребра BD. Например, если изначально у нас были треугольники ABC и ACD, то после применения операции будут треугольники ABD и BCD. Можно доказать, что если исходное разбиение не удовлетворяет критерию Делоне, то flip решает эту проблему. Однако для краткости я не буду приводить это доказательство здесь. Этот процесс повторяется до тех пор, пока все ребра не будут удовлетворять критерию Делоне

Связь с диаграммой Вороного

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

Обобщение Итеративный flip-алгоритм работает за $$$O(n^2)$$$ в худшем случае, так как в худшем случае требуется провести до (O(n)) операций флипа на каждый из $$$O(n)$$$ ребер, которые могут быть подвержены операции flip. Как уже говорилось ранее, существуют значительно более быстрые и точные способы найти триангуляцию Делоне, однако этот алгоритм более просто для понимания и его вполне можно написать за 30-40 минут.

Здесь я привожу реализацию алгоритма, однако хотя этот код и прошёл все тесты задачи 102482G - Panda Preserve, я всё же не стал бы ему доверять и на месте читателя написал бы его сам.

Code
Теги geometry, tutorial

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
en10 Английский VitaliiV 2025-12-03 08:52:29 19 Tiny change: 'am).\n\n**Generalization**\n\nThe ' -> 'am).\n\n**Summary**\n\nThe '
en9 Английский VitaliiV 2025-12-03 08:06:32 4
en8 Английский VitaliiV 2025-12-03 07:45:08 12
en7 Английский VitaliiV 2025-12-03 07:41:32 4
en6 Английский VitaliiV 2025-12-03 07:41:22 152
en5 Английский VitaliiV 2025-12-03 07:37:16 46 Tiny change: 'bout this х[here](htt' -> 'bout this [here](htt' (published)
en4 Английский VitaliiV 2025-12-03 07:32:36 57
en3 Английский VitaliiV 2025-12-03 07:31:40 7477 Tiny change: 's ABD and BCD. It can b' -> 's ABD and **BCD**. It can b'
en2 Английский VitaliiV 2025-12-03 07:24:10 102
en1 Английский VitaliiV 2025-12-03 07:21:34 4608 Initial revision for English translation (saved to drafts)
ru42 Русский VitaliiV 2025-12-02 20:28:55 2 Мелкая правка: ' алгоритм фортуна, пр' -> ' алгоритм Фортуна, пр'
ru41 Русский VitaliiV 2025-12-02 20:26:28 119
ru40 Русский VitaliiV 2025-12-02 20:19:02 4 Мелкая правка: 'о — такое раз' -> 'о — это такое раз'
ru39 Русский VitaliiV 2025-12-02 20:18:25 4
ru38 Русский VitaliiV 2025-12-02 20:18:12 13 Мелкая правка: ' операций флипа на каждый' -> ' операций flip на каждый'
ru37 Русский VitaliiV 2025-12-02 20:16:37 4
ru36 Русский VitaliiV 2025-12-02 20:16:25 44
ru35 Русский VitaliiV 2025-12-02 20:14:46 22
ru34 Русский VitaliiV 2025-12-02 20:13:45 80
ru33 Русский VitaliiV 2025-12-02 20:11:38 6 Мелкая правка: 'овести до \(O(n)\) операций ' -> 'овести до $O(n)$ операций '
ru32 Русский VitaliiV 2025-12-02 20:04:15 2 Мелкая правка: 'бщение**\nИтератив' -> 'бщение**\n\nИтератив'
ru31 Русский VitaliiV 2025-12-02 20:01:31 0 (опубликовано)
ru30 Русский VitaliiV 2025-12-02 20:01:03 1 Мелкая правка: ' проверить не наруша' -> ' проверить, не наруша'
ru29 Русский VitaliiV 2025-12-02 20:00:29 45
ru28 Русский VitaliiV 2025-12-02 19:59:10 255
ru27 Русский VitaliiV 2025-12-02 19:45:44 1 Мелкая правка: ' построить не исполь' -> ' построить, не исполь'
ru26 Русский VitaliiV 2025-12-02 19:44:58 1 Мелкая правка: 'зать о том что такое' -> 'зать о том, что такое'
ru25 Русский VitaliiV 2025-12-02 19:43:20 26
ru24 Русский VitaliiV 2025-12-02 19:42:43 125
ru23 Русский VitaliiV 2025-12-02 19:38:27 79
ru22 Русский VitaliiV 2025-12-02 19:37:44 116
ru21 Русский VitaliiV 2025-12-02 19:35:44 74
ru20 Русский VitaliiV 2025-12-02 19:32:56 2 Мелкая правка: 'полнить.\n![ ](htt' -> 'полнить.\n\n![ ](htt'
ru19 Русский VitaliiV 2025-12-02 19:32:40 173
ru18 Русский VitaliiV 2025-12-02 19:32:14 160
ru17 Русский VitaliiV 2025-12-02 19:31:58 2 Мелкая правка: '/ris1.png)На рисунке' -> '/ris1.png)\nНа рисунке'
ru16 Русский VitaliiV 2025-12-02 19:31:39 82
ru15 Русский VitaliiV 2025-12-02 19:31:23 238 Мелкая правка: 'полнить.\n\n![](https://' -> 'полнить.\n![ ](https://'
ru14 Русский VitaliiV 2025-12-02 19:27:11 382
ru13 Русский VitaliiV 2025-12-02 19:04:05 25
ru12 Русский VitaliiV 2025-12-02 19:03:40 8
ru11 Русский VitaliiV 2025-12-02 18:55:34 602
ru10 Русский VitaliiV 2025-12-02 18:50:10 7 Мелкая правка: '0%D1%84)\n[cut]\n\n\n**Ал' -> '0%D1%84)\n\n\n**Ал'
ru9 Русский VitaliiV 2025-12-02 18:49:59 4 Мелкая правка: 'n[cut]\n\n**Алго' -> 'n[cut]\n\n\n**Алго'
ru8 Русский VitaliiV 2025-12-02 18:49:42 8407 Мелкая правка: 'а $O(n^2)$в худшем с' -> 'а $O(n^2)$ в худшем с'
ru7 Русский VitaliiV 2025-12-02 18:38:24 784
ru6 Русский VitaliiV 2025-12-02 18:26:53 1490 Мелкая правка: '[0, 2 $\pi \)' -> '[0, 2 $\pi\)'
ru5 Русский VitaliiV 2025-12-02 18:07:01 5 Мелкая правка: '0%D1%84)\n\n**Свойст' -> '0%D1%84)\n[cut]\n**Свойст'
ru4 Русский VitaliiV 2025-12-02 18:06:43 376
ru3 Русский VitaliiV 2025-12-02 18:02:15 811 Мелкая правка: 'роного за O(n^2)\nТриангул' -> 'роного за $O(n^2)$\nТриангул'
ru2 Русский VitaliiV 2025-12-02 17:48:11 448
ru1 Русский VitaliiV 2025-12-02 17:38:34 43 Первая редакция (сохранено в черновиках)