Сообщество Engee

Выпуклая оболочка

Автор
avatar-maximsidorovmaximsidorov
Notebook

Алгоритм построения выпуклой оболочки

Рассматриваем реализацию алгоритма построения выпуклой оболочки (convex hull) множества точек в двумерном пространстве на языке Julia.

Введение

Алгоритм построения выпуклой оболочки — это геометрический алгоритм, который находит минимальный выпуклый многоугольник, содержащий все заданные точки. Проще говоря, представьте, что у вас есть набор гвоздей на доске, и вы натягиваете вокруг них резинку — форма этой резинки и будет выпуклой оболочкой.

Этот алгоритм используется во многих областях: компьютерной графике, распознавании образов, оптимизации, робототехнике и других. В данном примере мы используем библиотеку Polyhedra.jl для работы с многогранниками, а именно с V-представлением точек (Vertex representation), чтобы построить выпуклую оболочку точек.

Основная часть

Установка необходимых пакетов (если еще не установлены)

import Pkg; Pkg.add(["Polyhedra", "CDDLib"])

Подключение библиотек

Polyhedra — основная библиотека для работы с многогранниками

CDDLib — библиотека для вычислений с многогранниками через double description метод

using Polyhedra, CDDLib, Makie

Задаем набор точек в виде вершинного представления (V-representation).

Каждая точка — это координаты [x, y]

Dots = [
    [16,3],  [12,17], [0,6],  [-4,-6], [16,6],
    [16,-7], [16,-3], [17,-4], [5,19], [19,-8],
    [3,16],  [12,13], [3,-4], [17,5],  [-3,15],
    [-3,-9], [0,11],  [-9,-3], [-4,-2], [12,10]
];

vrep — это функция, создающая объект Vertex Representation из списка точек

A = vrep(Dots)
V-representation Polyhedra.PointsHull{Int64, Vector{Int64}, Int64}:
20-element iterator of Vector{Int64}:
 [16, 3]
 [12, 17]
 [0, 6]
 [-4, -6]
 [16, 6]
 [16, -7]
 [16, -3]
 [17, -4]
 [5, 19]
 [19, -8]
 [3, 16]
 [12, 13]
 [3, -4]
 [17, 5]
 [-3, 15]
 [-3, -9]
 [0, 11]
 [-9, -3]
 [-4, -2]
 [12, 10]

Создание объекта многогранника из V-представления

Используется бэкенд CDDLib для выполнения всех вычислений

P = polyhedron(A, CDDLib.Library())
Polyhedron CDDLib.Polyhedron{Float64}:
20-element iterator of Vector{Float64}:
 [16.0, 3.0]
 [12.0, 17.0]
 [0.0, 6.0]
 [-4.0, -6.0]
 [16.0, 6.0]
 [16.0, -7.0]
 [16.0, -3.0]
 [17.0, -4.0]
 [5.0, 19.0]
 [19.0, -8.0]
 [3.0, 16.0]
 [12.0, 13.0]
 [3.0, -4.0]
 [17.0, 5.0]
 [-3.0, 15.0]
 [-3.0, -9.0]
 [0.0, 11.0]
 [-9.0, -3.0]
 [-4.0, -2.0]
 [12.0, 10.0]

Построим точки и выпуклый многогранник:

# Создаем сетку (mesh) из многогранника P
m = Polyhedra.Mesh(P);

# Извлекаем координаты X и Y из вектора точек Dots
X = [dot[1] for dot in Dots]  # Первые элементы - координаты X
Y = [dot[2] for dot in Dots]  # Вторые элементы - координаты Y

# Создаем фигуру и оси для графика
fig = Figure()
ax = Axis(fig[1, 1])

# Рисуем точки на графике
Makie.scatter!(ax, X, Y, markersize=8, color=:red)

# Рисуем сетку многогранника полупрозрачным синим цветом
Makie.mesh!(m, color=(:blue,0.5))

# Отображаем фигуру
fig
No description has been provided for this image

Построение выпуклой оболочки многогранника P с самим собой (по сути — получаем ту же самую выпуклую оболочку)

Эта операция создает новый объект, представляющий выпуклую оболочку двух многогранников

Pch = convexhull(P, P)
Polyhedron CDDLib.Polyhedron{Float64}:
40-element iterator of Vector{Float64}:
 [16.0, 3.0]
 [12.0, 17.0]
 [0.0, 6.0]
 [-4.0, -6.0]
 [16.0, 6.0]
 [16.0, -7.0]
 [16.0, -3.0]
 [17.0, -4.0]
 [5.0, 19.0]
 [19.0, -8.0]
 [3.0, 16.0]
 [12.0, 13.0]
 [3.0, -4.0]
 [17.0, 5.0]
 [-3.0, 15.0]
 [-3.0, -9.0]
 [0.0, 11.0]
 [-9.0, -3.0]
 [-4.0, -2.0]
 [12.0, 10.0]
 [16.0, 3.0]
 [12.0, 17.0]
 [0.0, 6.0]
 [-4.0, -6.0]
 [16.0, 6.0]
 [16.0, -7.0]
  ⋮

Удаление избыточных точек/вершин из представления

То есть остаются только вершины, которые действительно нужны для описания границ выпуклого многоугольника

removevredundancy!(Pch)

Вывод результата на экран - информации о многограннике (вершины, ребра и т.д.)

println("$Pch")
convexhull([5.0, 19.0], [19.0, -8.0], [17.0, 5.0], [-3.0, 15.0], [-9.0, -3.0], [12.0, 17.0], [-3.0, -9.0])

Что происходит шаг за шагом?

  1. Мы задаем набор точек плоскости.
  2. Формируем по этим точкам V-представление — способ описания фигуры через ее вершины.
  3. Создаем объект типа "многогранник", который хранится во внутреннем формате библиотеки Polyhedra.
  4. Строим выпуклую оболочку (в данном случае фактически копируем её, так как объединяем многогранник с самим собой).
  5. Из полученного объекта удаляем избыточные точки — оставляем только те, что реально определяют границы фигуры.
  6. Печатаем результат: информация о вершинах выпуклого многоугольника будет выведена.

Заключение

Мы рассмотрели простой пример использования библиотеки Polyhedra.jl языка программирования Julia для построения выпуклой оболочки набора точек. Благодаря мощным возможностям этой библиотеки и поддержке различных бэкендов, можно легко строить, анализировать и визуализировать полиэдральные структуры. Это может быть полезно при решении задач из линейного программирования, компьютерной графики и аналитической геометрии.

Подводя итог, данный пример позволяет новичку понять принципы работы с геометрическими данными и многогранниками в Julia.

Пример разработан с использованием материалов Rosetta Code