给定凸多边形,如何找到定义具有最大面积的三角形的3个点。
相关:该三角形的外接圆是否也会定义多边形的最小边界圆?
答案 0 :(得分:34)
是的,你可以做得比蛮力更好。
蛮力我认为你的意思是检查所有三点,并选择一个最大面积的点。这在 O(n 3 )时间中运行,但事实证明它不仅可以在O(n 2 )中进行但在 O(n)时间 !
[更新: 2017年发表的一篇论文通过示例显示O(n)解决方案不适用于特定类别的多边形。有关详细信息,请参阅this答案。但是O(n 2 )算法仍然是正确的。]
如果需要,首先对点进行排序/计算凸包(在O(n log n)时间内),我们可以假设我们有凸多边形/船体,其点按照它们在多边形中出现的顺序循环排序。调用点1,2,3,...,n。令(变量)点A,B和C分别从1,2和3开始(以循环次序)。我们将移动A,B,C直到ABC是最大面积三角形。 (这个想法类似于计算rotating calipers时使用的diameter (farthest pair)方法。)
A和B固定,前进C(例如,最初,A = 1,B = 2,C前进到C = 3,C = 4,......)只要三角形的面积增加,即,只要区域(A,B,C)≤区域(A,B,C + 1)。这个点C将是那些固定A和B最大化区域(ABC)的点。(换句话说,函数Area(ABC)是单峰作为C的函数。)
接下来,如果增加面积,则前进B(不改变A和C)。如果是这样,再次按上述方式推进C.然后如果可能的话再次前进B等。这将给出最大区域三角形,其中A作为顶点之一。
(到此为止的部分应该很容易证明,而且对每个A单独执行此操作会得到一个O(n 2 )算法。)
现在再次推进A,如果它改善区域,依此类推。(这部分的正确性更加微妙:Dobkin和Snyder在他们的论文中给出了证据,但最近的一篇论文显示了一个反例。我还没有理解。)
虽然这有三个“嵌套”循环,但请注意B和C总是向前推进,并且它们总共最多前进2n次(类似A最多前进n次),所以整个过程在O中运行( n)时间。
Python中的代码片段(转换为C应该很简单):
# Assume points have been sorted already, as 0...(n-1)
A = 0; B = 1; C = 2
bA= A; bB= B; bC= C #The "best" triple of points
while True: #loop A
while True: #loop B
while area(A, B, C) <= area(A, B, (C+1)%n): #loop C
C = (C+1)%n
if area(A, B, C) <= area(A, (B+1)%n, C):
B = (B+1)%n
continue
else:
break
if area(A, B, C) > area(bA, bB, bC):
bA = A; bB = B; bC = C
A = (A+1)%n
if A==B: B = (B+1)%n
if B==C: C = (C+1)%n
if A==0: break
该算法在Dobkin和Snyder, On a general method for maximizing and minimizing among certain geometric problems ,FOCS 1979中得到证明,上面的代码是他们的ALGOL-60代码的直接翻译。为休息时间结构道歉;它应该可以将它们转换为更简单的while循环。
答案 1 :(得分:4)
回答您的相关问题:
三角形的外接圆不一定是多边形的最小边界圆。为了看到这一点,考虑一个非常平坦的等腰三角形,比如顶点为(0,0),(10,0)和(5,1)。最小边界圆具有中心(5,0)和半径5,但是该圆不接触(5,1)处的顶点,因此它不是外接圆。 (外接圆具有中心(5,-12)和半径13)
修改强>
选择较小的外接圆或包含多边形直径的对映点的圆也是不够的,因为可以构造具有在最大三角形的外接圆外面的点的多边形。考虑具有顶点的五边形:
(-5, 0)
(-4, -1)
( 5, 0)
( 4, 1)
(-4, 1)
最大三角形的顶点为(-4,-1),(5,0)和(-4,1)。它的外接圆不包括(-5,0)处的点。
答案 2 :(得分:3)
根据this论文,有一类凸多边形,其中ShreevatsaR的答案引用的算法失败了。本文还提出了一种解决问题的O(n log n)分而治之算法。
显然,在所有 A中移动B点和C点的简单O(n 2 )算法仍然有效。
答案 3 :(得分:0)
三角形的面积= sqrt((a + b-c)(a-b + c)( - a + b + c)*(a + b + c))/ 4 如果使用c连接到凸多边形的端点 如果a和b会碰到凸多边形 你可以迭代你的多边形 允许a增长,b缩小,直到找到最大区域。 我会从中间点开始尝试每个方向以获得更大的区域。
答案 4 :(得分:0)
我知道这是一篇旧帖子,但通过参考answer above,我能够修改代码以最大化n边多边形的面积。
注意:使用Emgu OpenCV library找到了凸包。我使用CvInvoke.ContourArea()
方法计算多边形的给定面积。这是用C#编写的。它还假设点按顺时针顺序排列。这可以在方法CvInvoke.ConvexHull()
中指定。
private PointF[] GetMaxPolygon(PointF[] convexHull, int vertices)
{
// validate inputs
if(convexHull.Length < vertices)
{
return convexHull;
}
int numVert = vertices;
// triangles are the smalles polygon with an area.
if (vertices < 3)
numVert = 3;
PointF[] best = new PointF[numVert]; // store the best found
PointF[] next = new PointF[numVert]; // test collection of points to compare
PointF[] current = new PointF[numVert]; // current search location.
int[] indexes = new int[numVert]; // map from output to convex hull input.
int[] nextIndices = new int[numVert];
//starting values 0,1,2,3...n
for(int i = 0; i < numVert; i++)
{
best[i] = convexHull[i];
next[i] = convexHull[i];
current[i] = convexHull[i];
}
// starting indexes 0,1,2,3... n
for(int i = 0; i < numVert; i++)
{
nextIndices[i] = i;
indexes[i] = i;
}
// starting areas are equal.
double currentArea = GetArea(current);
double nextArea = currentArea;
int exitCounter = 0;
while(true)
{
// equivelant to n-1 nested while loops
for(int i = numVert - 1; i > 0; i--)
{
while (exitCounter < convexHull.Length)
{
// get the latest area
currentArea = GetArea(current);
nextIndices[i] = (nextIndices[i] + 1) % convexHull.Length;
next[i] = convexHull[nextIndices[i]]; // set the test point
nextArea = GetArea(next);
if (currentArea <= nextArea) // compare.
{
indexes[i]= (indexes[i]+1) % convexHull.Length;
current[i] = convexHull[indexes[i]];
currentArea = GetArea(current);
exitCounter++; // avoid infinite loop.
}
else //stop moving that vertex
{
for(int j = 0; j< numVert; j++)
{
nextIndices[j] = indexes[j];
next[j] = convexHull[indexes[j]];//reset values.
}
break;
}
}
}
// store the best values so far. these will be the result.
if(GetArea(current)> GetArea(best))
{
for (int j = 0; j < numVert; j++)
{
best[j] = convexHull[indexes[j]];
}
}
// The first index is the counter. It should traverse 1 time around.
indexes[0] = (indexes[0] + 1) % convexHull.Length;
for(int i = 0; i < vertices-1;i++)
{
if(indexes[i] == indexes[i+1])// shift if equal.
{
indexes[i + 1] = (indexes[i + 1] + 1) % convexHull.Length;
}
}
//set new values for current and next.
for(int i = 0; i < numVert; i++)
{
current[i] = convexHull[indexes[i]];
next[i] = convexHull[indexes[i]];
}
// means first vertex finished traversing the whole convex hull.
if (indexes[0] == 0)
break;
}
return best;
}
使用的区域方法。这可能会根据最大化需要而改变。
private double GetArea(PointF[] points)
{
return CvInvoke.ContourArea( new Emgu.CV.Util.VectorOfPointF(points),false);
}