长版
二分查找被认为是在一些地方的解决方案,但它只适用于某些情况。
顶点的分布可以以许多不同的方式变化。您可以有许多聚集在一个点附近,另一个孤立地存在,您可以有形成抛物线形状的顶点(以您的图表为例,删除顶点 7、8 和 9),您可以具有类似对数的分布(例如仅顶点 1、2、3 和 4),或者真正的其他数量的可能性。在所有这些不同的情况中,您将拥有不同数量和位移的局部最大值和最小值。
很有可能您需要组合方法来估计分布,然后应用适合类型分布的策略。
让我们尝试描述这样的策略:
首先,请记住您有一个这样的顶点数组,以严格顺序列出,以逆时针旋转。这很重要,并且是随后所有假设和推理的基础。
观察 V[n]
的行为。如果V[n]
具有 y 坐标V[n].y
小于 V[1]
的 y 坐标,或者 V[n].y < V[1].y
,您可以得出结论,所有其他顶点V[2,n-1]
的 y 坐标也必须低于V[1]
(考虑为什么这必须是这种情况)。因此,V[1]
具有最大的 y 坐标。
现在,剩下的分析需要我们改变多边形的概念模型,以简化其表示及要解决的问题。而不是绘制点 (V[i].x, V[i].y)
以获得多边形的形状,相反绘制 (i, V[i].y)
以代表一种想象的连续函数 f(i) = V[i].y
。现在我们问题的解决方案是找到函数f(i) = V[i].y
的全局最大值的解决方案。
考虑到这一点,对于所有其他情况,其中V[n].y > V[1].y
,我们必须执行二分查找,但我们有两种可能的情况:
V[2]
的 y 坐标小于 V[1]
。V[2]
的 y 坐标大于 V[1]
。这很重要,因为情况 1 告诉我们V[1]
不是局部最小值,而情况 2 告诉我们V[1]
是局部最小值(再次考虑为什么这必须是这种情况)。
V [1]
是一个局部最小值。这意味着只能在V [n]
处有一个附加的局部最小值,或者根本没有其他局部最小值。因此,我们可以执行二进制搜索或类似二进制的搜索,以便逐渐收敛于曲线上唯一的局部最大值。V [1]
不是局部最小值,而是局部最大值。更重要的是,您有两个可能的局部最大值,它们位于V [1]
和V [n-k]
处,其中n-k> 1
。为了帮助可视化,如果绘制函数f(i)= V [i] .y
的点,您将看到抛物线形状或正弦形状。因此,第二个局部最大值在V [n-k]
处将是抛物线的最右端,或正弦曲线的峰值。V [n]
具有比V [n-1]
更大的y坐标,则V [n]
必须是第二个局部最大值(再次考虑为什么这必须是真的),实际上,我们可以立即确定V [n]
具有最大的y坐标。否则,存在一些k使得V [n-k]
是我们的局部最大值,这意味着我们需要执行搜索。V [1]
(我们需要找到一个局部最大值,因为V [1]
是一个局部最大值,我们可能会意外地收敛它)。V [i]
,如果V [i] .y < V [1] .y
,则向V [n]
收敛。V [i] .y > V [1] .y
,则朝着增加y的方向收敛(只需比较V [i-1]
和V [i + 1]
与V [i]
即可)。log(n)
时间内隔离该值。V [1] .y <V [n] .y
的两种不同情况,让我们注意到这个约束二进制搜索在案例2中的效果与在案例1中一样准确。因此,我们可以通过遵循受限制的二进制搜索的规则来概括对案例1和案例2的搜索。这显着降低了算法复杂度。V[1].y < V[n].y
并且V[n].y > V[n-1].y
,那么V[n].y
是最大值。V[1].y < V[n].y
并且V[n].y < V[n-1].y
,则执行约束二分查找。O(1)
边缘情况和一个标准的O(log n)
情况。
无优化解决方案
如果V[1].y > V[n].y
,那么V[1].y
是最大值。V[1].y < V[n].y
,则执行约束二分查找。O(1)
边缘情况和一个标准的O(log n)
情况。V[1].y < V[2].y
的情况下,对于任何 i > 2
,我们必须有 V[i].y > V[2].y
。如果相反,我们有 V[i].y < V[2].y
,那么多边形就不能是凸多边形。但是,在我的概念模型中,它们不会同时成为局部最小值。我将修正我的答案以反映这一点。 - B. Fleming让V[m]为具有最大y坐标的顶点。
最简单的情况是考虑,当V[2].y < V[1].y > v[n].y
时。由于排除了这种情况,后续的推理会更简单,我们将假设已经对此进行了初步检查。
考虑一条以起点V[i]为原点的边E[i],其中1。给定所有x和y坐标都不同的约束条件,E[i]必须位于以下4个平面象限之一:
鉴于我们已经排除了 m=i=1
的情况,对于 E[i] 位于第一、二或四象限的情况,必须满足 m>i
。如果 E[i] 位于第三象限,则有 m=i
,当且仅当 V[i].y > V[i-1].y
为真,或者 m<i
。
我们可以将此推理用作二分搜索的基础,在每次迭代中执行以下操作:
if E[i] lies in Quadrant III
if V[i].y > V[i-1].y then m=i
else consider left half
else
consider right half
这里有一些Java代码来说明:
static Point maxY(Point[] v)
{
// check for max at origin
if(v[1].y < v[0].y && v[v.length-1].y < v[0].y)
{
return v[0];
}
int left = 0;
int right = v.length-1;
Point maxY = null;
while(left <= right)
{
int mid = left + (right-left)/2;
if(v[(mid+1)%v.length].y < v[mid].y && v[(mid+1)%v.length].x < v[mid].x)
{
// Quadrant III
if(v[mid].y > v[mid-1].y)
{
maxY = v[mid];
break;
}
right = mid - 1;
}
else
{
left = mid + 1;
}
}
return maxY;
}
还有一些简单的测试用例:
public static void main(String[] args)
{
Point[][] tests = {
{new Point(0, 10), new Point(10, 0), new Point(9, 5)},
{new Point(0, 0), new Point(9, 5), new Point(10, 10)},
{new Point(0, 0), new Point(10, 10), new Point(5, 8)},
{new Point(0, 5), new Point(9, 0), new Point(10, 10)},
{new Point(0, 5), new Point(6,0), new Point(10, 6), new Point(5,10)}};
for(Point[] coords : tests)
System.out.println(maxY(coords) + " : " + Arrays.toString(coords));
}
输出:
(0, 10) : [(0, 10), (10, 0), (9, 5)]
(10, 10) : [(0, 0), (9, 5), (10, 10)]
(10, 10) : [(0, 0), (10, 10), (5, 8)]
(10, 10) : [(0, 5), (9, 0), (10, 10)]
(5, 10) : [(0, 5), (6, 0), (10, 6), (5, 10)]