Выпуклая оболочка минимального периметра подмножества набора точек
учитывая n точек на плоскости. 3 не лежат на одной прямой.
учитывая число k.
найти подмножество K точек, так что выпуклая оболочка K точек имеет минимальный периметр из любой выпуклой оболочки подмножества k точек.
Я могу думать о наивном методе, выполняемом в O (n^k K log k). (Найдите выпуклую оболочку каждого подмножества размера k и выведите минимум).
Я думаю, что это проблема NP, но я не могу найти ничего подходящего для сокращения к.
у кого есть идеи по этой проблеме?
пример
the set of n=4 points {(0,0), (0,1), (1,0), (2,2)} and k=3
результат:
{(0,0),(0,1),(1,0)}
поскольку этот набор содержит 3 точки, выпуклая оболочка и периметр результата меньше, чем у любых других наборов из 3 точек.
4 ответов
Это можно сделать в O(kn^3) времени и o (kn^2) пространстве(или, возможно, O (kn^3), Если вы хотите фактические точки).
эта бумага:http://www.win.tue.nl / ~gwoegi / документы / area-k-gons.pdf
по Eppstein et al, имеет алгоритмы для решения этой проблемы для минимального периметра и других весовых функций, таких как площадь, сумма внутренних углов и т. д., которые следуют определенным ограничениям, хотя название говорит о минимальной площади (см. следствие 5.3 для периметра).
в основная идея динамического программирования заключается в следующем (прочитайте первые несколько абзацев раздела 4):
предположим, что S-заданное множество точек, а Q-выпуклая оболочка K точек с минимальным периметром.
пусть p1-самая нижняя точка Q, p2 и p3-следующие точки На корпусе в порядке против часовой стрелки.
мы можем разложить Q на треугольник p1p2p3 и выпуклую оболочку из K-1 точек Q' (которая разделяет сторону p1p3 с треугольником p1p2p3).
основное наблюдение заключается в том, что Q' является оптимальным для k-1, в котором самая нижняя точка-p1, а следующая точка-p3, и все точки Q' лежат на одной стороне линии p2 - >p3.
таким образом, поддерживая 4D массив оптимальных многоугольников для каждого квадруполя (pi, pj, pk, m) такой, что
- многоугольник является выпуклой оболочкой ровно m точек S.
- pi-самая нижняя точка многоугольника.
- pj является следующая вершина в порядке против часовой стрелки,
- все точки многоугольника лежат слева от линии pi - > pj.
- все точки лежат на той же стороне pj->pk, что и pi.
может помочь нам найти оптимальные многоугольники для m=k, учитывая оптимальные многоугольники для m
в статье точно описывается, как это сделать, чтобы достичь заявленных границ пространства и времени.
надеюсь, это поможет.
это не совсем красивое решение. На самом деле, это довольно сложно реализовать, но это, безусловно, дает полиномиальную сложность. Хотя сложность также велика (N^5*k-моя приблизительная оценка), кто-то может найти способ улучшить ее или найти здесь идею для лучшего решения. Или вам может быть достаточно: даже эта сложность намного лучше, чем bruteforce.
Примечание: оптимальное решение (set S
) с КАСКО H
включает все точки из исходного набора внутри H
. В противном случае, мы могли бы выбросить одну из пограничных пунктов H
и включите эту пропущенную точку, уменьшив периметр.
(обновление так же, как "оптимизация" mbeckish отправлено)
предположение: нет двух точек из набора образуют вертикальную линию. Это может быть легко достигнуто путем поворота всего набора точек на некоторый иррациональный угол вокруг начала координат.
из-за предположения выше, любой сложный корпус есть одна левая и одна правая точка. Кроме того, эти две точки делят корпус на top
и bottom
запасные части.
теперь давайте возьмем один сегмент из top
часть этого корпуса и один из bottom
часть. Назовем эти два сегмента middle segments
и периметр правой части этого корпуса -right
периметра.
Примечание: эти два сегмента-это все, что нам нужно знать о правой части нашей выпуклой оболочки, чтобы продолжать строить ее слева. Но только два очка вместо 4-х не хватает: мы не можем поддерживать состояние 'convexness' таким образом.
это приводит к решению. Для каждого набора точек {p0, p1, p2, p3} и числа i
(i right периметр, который может быть достигнут, если [p0, p1], [p2, p3] два middle
сегментов и i
- это количество точек в right
часть этого решения (включая те, что внутри него, а не только на границе).
идем через все точки справа налево. Для каждого нового пункта p
мы проверяем все комбинации точек {p0, p1, p2, p3} такие, что точка p может продолжать этот корпус влево (либо на top
или bottom
часть). Для каждого такого набора и размера i
, мы уже храним оптимальный размер периметра (см. пункт выше).
Примечание: если добавить пункт p
до right-hull
сформированный точками {p0, p1, p2, p3}, вы увеличите размер набора i
по крайней мере на 1. Но иногда это число будет > 1: вам нужно будет включить все точки в треугольник {p, p0, p2}. Они не на корпусе, а внутри.
алгоритм закончен :) кроме того, несмотря на страшную сложность, вы можете заметить, что не все сегменты [p0, p1], [p2, p3] могут быть middle
сегменты: это должно существенно сократить фактическое время вычислений.
обновление это обеспечивает только оптимальный размер периметра, а не сам набор. Но найти набор очень просто: для каждого "состояния" над вами сохраняется не только размер периметра, но и последняя добавленная точка. Затем вы можете "отследить" свое решение. Это довольно стандартный трюк, я полагаю, это не проблема для Вас, Вы, кажется, хорошо разбираетесь в алгоритмах:)
обновление 2 это по существу DP (динамическое программирование), только немного раздутый
одна возможная оптимизация: вы можете игнорировать любые подмножества, выпуклая оболочка которых содержит точки, не входящие в подмножество.
доказательство:
Если ваш выпуклый корпус содержит точки, которые не находятся в вашем подмножестве, то удалите точку из вашего подмножества, которое находится на корпусе, и замените ее точкой внутри корпуса. Это даст корпус равного или меньшего периметра.
в плоском случае вы можете использовать алгоритм, известный как Jarvis march, который имеет наихудшую сложность o(n^2). В этом алгоритме вы начинаете строить корпус в произвольной точке, а затем проверяете, какая точка должна быть добавлена следующей. Псевдокод взят из Википедия:
jarvis(S)
pointOnHull = leftmost point in S
i = 0
repeat
P[i] = pointOnHull
endpoint = S[0] // initial endpoint for a candidate edge on the hull
for j from 1 to |S|-1
if (S[j] is on left of line from P[i] to endpoint)
endpoint = S[j] // found greater left turn, update endpoint
i = i+1
pointOnHull = endpoint
until endpoint == P[0] // wrapped around to first hull point
насколько я понимаю, выпуклые корпуса уникальны для каждого набора точек, поэтому нет необходимости искать минимум. Вы просто найдете один, и он будет самым маленьким определение.
редактировать
опубликовано решение решает для выпуклой оболочки с наименьшим количеством очков. Любой корпус с большим количеством точек будет иметь более длинный периметр, и я неправильно понял вопрос о поиске минимального периметра, а не минимального периметра для набора с K точками.
эта новая проблема, вероятно, NP, как и предполагалось, и наиболее похожа на проблему с самым длинным путем. К сожалению, мне не хватает изобретательности, чтобы обеспечить достойное сокращение.