Учитывая набор 2D-точек X, я хотел бы найти выпуклую оболочку, состоящую из максимальных n точек. Конечно, это не всегда возможно. Поэтому я ищу приблизительную выпуклую оболочку, состоящую из макс. n точек, максимально покрывающих множество точек X.
Говоря более формально, если F(H,X) возвращает количество точек X, покрываемых выпуклой оболочкой H, где |H| количество точек, из которых построен корпус, то я ищу следующее количество:
H_hat = argmax_H F(H,X), s.t |H|<=n
Другим способом решения задачи является задача нахождения полигона, состоящего из макс. n углов множества X так, чтобы оно максимально покрывало указанное множество X.
Я придумал следующее:
X = getSet() \\ Get the set of 2D points
H = convexHull(X) \\ Compute the convex hull
while |H| > n do
n_max = 0
for h in H:
H_ = remove(h,H) \\ Remove one point of the convex hull
n_c = computeCoverage(X,H_) \\ Compute the coverage of the modified convex hull.
\\ Save which point can be removed such that the coverage is reduced minimally.
if n_c > n_max:
n_max = n_c
h_toremove = h
\\ Remove the point and recompute the convex hull.
X = remove(h,X)
H = convexHull(X)
Однако это очень медленный способ сделать это. У меня большой набор точек, а n (ограничение) маленькое. Это означает, что исходная выпуклая оболочка содержит много точек, поэтому цикл while выполняется очень долго. Есть ли более эффективный способ сделать это? Или есть другие предложения по приближенным решениям?
Эй, я добавил вычислительную геометрию в качестве тега, так как знаю по крайней мере одного эксперта, который специально ей следует. Вы проделали отличную работу с тегами, поэтому мне пришлось исключить слово «выпуклый». Надеюсь, что все в порядке; вы можете внести коррективы по желанию.
Основная трудность заключается в том, что размер корпуса не уменьшается монотонно при удалении точек.
Сомневаюсь, что оптимальное решение можно найти постепенно. Представьте случай H=3 и предположим, что вы нашли треугольник, покрывающий 5 точек. Может быть, есть еще один в совершенно другом месте, вмещающий 100 из них.
@YvesDaoust Возможно, вы правы, но единственные точные решения, которые я могу придумать, намного медленнее, чем предложение в вопросе.
@DavidEisenstat: я имею в виду, что проблема, похоже, не имеет хороших локальных свойств. Изменение одной вершины может изменить 90% покрытия.
@YvesDaoust, весьма вероятно, что написанный мной алгоритм не вычисляет оптимальное решение. Это лучшее, что я придумал, думая об этом. Теперь я рассмотрю идеи ниже.
@DavidEisenstat Спасибо за настройку тегов.
Я думал о подходе, в котором вы развиваете выпуклый многоугольник с H-сторонами и пытаетесь расширить его жадным способом, чтобы увеличить покрытие. Другое мое замечание показывает, что это, вероятно, безнадежно, поскольку истинный оптимум может не иметь абсолютно никакой общей точки.





Возможно, вам подойдет следующий подход: сначала вычислите выпуклую оболочку H. Затем выберите подмножество точек n случайным образом из H, который строит новый многоугольник, который может не покрывать все точки, поэтому назовем его квазивыпуклой оболочкой Q. Подсчитайте, сколько точек содержится в Q (инлайнеры). Повторите это определенное количество раз и оставьте предложение Q с наибольшим количеством вставок.
Это кажется немного связанным с RANSAC, но для этой задачи у нас нет представления о том, что такое выброс, поэтому мы не можем реально оценить коэффициент выбросов. Следовательно, я не знаю, насколько хорошим будет приближение или сколько итераций вам нужно, чтобы получить разумный результат. Может быть, вы можете добавить некоторые эвристики вместо того, чтобы выбирать точки n чисто случайным образом, или вы можете установить пороговое значение того, сколько точек должно по крайней мере содержаться в Q, а затем остановиться, когда вы достигнете этого порога.
Редактировать
На самом деле, подумав об этом, вы могли бы использовать подход RANSAC:
max_inliers = 0
best_Q = None
while(true):
points = sample_n_points(X)
Q = construct_convex_hull(points)
n_inliers = count_inliers(Q, X)
if n_inliers > max_inliers:
max_inliers = n_inliers
best_Q = Q
if some_condition:
break
Преимущество этого заключается в том, что создание выпуклой оболочки происходит быстрее, чем в вашем подходе, так как он использует максимум точек n. Кроме того, проверка количества вставок должна быть быстрой, поскольку это может быть просто набор сравнений знаков с каждой стороной выпуклой оболочки.
Пара идей.
Точки, которые мы исключаем при удалении вершины, лежат в треугольнике, образованном этой вершиной и двумя смежными с ней вершинами на выпуклой оболочке. Удаление любой другой вершины не влияет на набор потенциально исключаемых точек. Таким образом, нам нужно всего лишь дважды пересчитать покрытие для каждой удаленной вершины.
Говоря о пересчете покрытия, мы не обязательно должны рассматривать каждую точку. Эта идея не улучшает худший случай, но я думаю, что на практике это должно быть большим улучшением. Поддерживайте индекс точек следующим образом. Выберите случайную вершину, которая будет «корнем», и сгруппируйте точки, по которым треугольник, образованный корнем, и две другие вершины содержат их (время O (m log m) с хорошим алгоритмом). Всякий раз, когда мы удаляем некорневую вершину, мы объединяем и фильтруем два набора точек для треугольников, включающих удаленную вершину. Всякий раз, когда мы пересчитываем покрытие, мы можем сканировать только точки в двух соответствующих треугольниках. Если мы когда-нибудь удалим этот корень, выберем новый и переделаем индекс. Общая стоимость этого обслуживания составит O(m log^2 m) в ожидании, где m — количество баллов. Однако оценить стоимость вычислительного покрытия сложнее.
Если точки достаточно равномерно распределены внутри корпуса, можно использовать площадь в качестве прокси для покрытия. Сохраните вершины в очереди приоритетов, упорядоченной по площади их треугольника, образованного их соседями (ухом). Всякий раз, когда мы удаляем точку, обновляем область уха двух ее соседей. Это алгоритм O (m log m).
Следующее не решает вопрос в том виде, в каком я его сформулировал, но решает проблему, породившую вопрос. Поэтому я хотел добавить его на случай, если кто-то еще столкнется с чем-то подобным.
Я исследовал два подхода:
1) Рассматривает выпуклую оболочку как многоугольник и применяет к ней алгоритм упрощения многоугольника. Конкретным алгоритмом, который я исследовал, был Алгоритм Рамера-Дугласа-Пекера.
2) Применить алгоритм, описанный в вопросе, без пересчета выпуклой оболочки.
Оба подхода не дадут вам (насколько я могу судить) желаемого решения поставленной задачи оптимизации, но для моих задач они работали достаточно хорошо.
Должен ли
H_hatсостоять из исходных точек или можно добавить новые?