【问题标题】:Find max y coordinate of a convex polygon查找凸多边形的最大 y 坐标
【发布时间】:2019-02-14 08:21:07
【问题描述】:

我有一个数组V[1,2,....,n],其中数组的每个元素以坐标对 (x,y) 的形式表示凸多边形的一个顶点。

假设V[1] 是具有最小x 坐标的顶点,并且顶点V[1,2,....,n] 是逆时针排列的,如图所示。还假设顶点的 x 坐标都是不同的,顶点的 y 坐标也是如此。

现在,我想找到具有最大 y 坐标值的顶点。我们都知道朴素的 O(n) 方法,但是有可能在 O(log(n)) 中找到它吗?

我使用V[1] 是具有最小x 坐标的顶点的信息在O(log(n)) 时间内找到具有最大x 坐标的顶点。但是有可能做到最大y坐标吗?

感谢您的帮助!

【问题讨论】:

  • 你能在 V[1] 和最大 x 坐标的顶点之间运行 ternary search 吗?
  • @PeterdeRivaz 我对三元搜索一无所知。它会起作用吗?
  • 我想是这样,但我可能没有正确理解这个问题。我假设您使用三元搜索也找到了最大 x 坐标,因此您会熟悉该技术。你用什么方法来找到最大 x?也许它也有助于找到最大 y?
  • @PeterdeRivaz 我利用中间两个元素之间的差异来找到方向并沿着增加的方向移动,每次迭代将数组大小减半
  • 看起来您找到了比三元更好的方法,我已经回答了一个链接,该链接将您的方法推广到任意方向。

标签: algorithm geometry max convex convex-polygon


【解决方案1】:

加长版

这里有几个地方注意到二分搜索是解决方案,但它只适用于某些情况。

顶点的分布可以以多种不同的方式变化。您可以在一个点附近聚集许多,而在其他地方隔离一个,您可以有形成抛物线形状的顶点(以您的图表为例,消除顶点 7、8 和 9),您可以有类似对数的分布(例如只有顶点 1、2、3 和 4),或者实际上是任何其他数量的可能性。在所有这些不同的情况下,局部最大值和最小值的数量和位移都会有所不同。

您可能需要多种方法来估计分布,然后应用适合分布类型的策略。

让我们试着描述一下这样的策略:

首先,请记住,您有一个这样的顶点数组,按逆时针旋转的严格顺序列出。这很重要,也是所有进一步假设和推理的基础。

观察V[n] 的行为。如果V[n] 的y 坐标V[n].y 小于V[1]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 的所有其他情况,我们必须执行二进制搜索,但我们有两种可能的情况需要考虑:

  1. V[2] 的 y 坐标小于 V[1]
  2. V[2] 的 y 坐标大于 V[1]

这很重要,因为案例 1 告诉我们 V[1] 不是 局部最小值,而案例 2 告诉我们 V[1] 局部最小值(一次再考虑一下为什么一定会这样)。

由于V[1] 是局部最小值,案例 2 是一个不错的简单案例。这意味着在V[n] 处只能有一个额外的局部最小值,或者根本没有其他局部最小值。因此,我们可以执行二进制或类似二进制的搜索,以便我们逐渐收敛于曲线上唯一的局部最大值。

您的图表是案例 1 的示例,这是较难的案例。 V[1] 不是局部最小值,所以它是局部最大值。更重要的是,您有两个可能的局部最大值,它们位于V[1]V[n-k],其中n-k > 1。为了帮助可视化,如果您绘制函数f(i) = V[i].y 的点,您将看到抛物线形状或正弦形状。因此,V[n-k] 处的第二个局部最大值将是抛物线的最右端,或者是正弦曲线的峰值。

(注意:本段是一个可选的优化步骤。) 让我们考虑如何确定我们正在处理哪种类型的局部最大值:如果 V[n] 的 y 坐标大于 @ 987654349@,然后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]V[i-1]V[i+1])。

这应该允许您安全地收敛到最右边的局部最大值并在log(n) 时间内隔离该值。

现在我们已经介绍了V[1].y < V[n].y 的两种不同情况,请注意,这种受约束的二分搜索在情况 2 中的工作与在情况 1 中一样准确。因此,我们可以对这两种情况的搜索进行概括1 和案例 2 通过遵循这两种情况的约束二分搜索规则。这大大降低了算法的复杂度。

总的来说,您应该能够在任何一般情况下达到O(log n) 时间,以及几个O(1) 边缘情况。

总结

这个问题背后的技巧是解构多边形的概念,绘制点(i, V[i].y) 而不是(V[i].x, V[i].y),并将这些点想象成一个连续函数。那么这个问题的解决方案就变成了“f(i) = V[i].y 的全局最大值是多少?”这个问题的解决方案。由于凸多边形的属性和顶点的排序方式,我们可以确定V[1] 绝对是一个局部最大值。考虑到这一点,V[1] 要么是全局最大值,要么不是,我们可以在一开始就在恒定时间内确定这一点。如果它不是全局最大值,那么我们可以执行约束二进制搜索,以防止我们在V[1] 上收敛,从而允许我们确定对数时间内的全​​局最大值。如果我们想要更加复杂,我们还可以确定 V[n] 是否是恒定时间内的全​​局最大值作为额外的优化步骤。


短版

V[1].y > V[n].y 时,最大值为V[1].y。您的解决方案应仅在 V[1].y < V[n].y 的情况下使用二进制搜索。给定任意V[i],此二分搜索必须遵守以下约束:

  • 基本情况:如果V[1].y > V[i].y,向V[n]方向收敛。
  • 标准情况:如果V[i].y < V[i+1].y,向V[n]方向收敛; else if V[i].y < v[i-1].y,向V[1]方向收敛;否则V[i].y 是最大值。

还可以针对V[1].y < V[n].yV[n].y > V[n-1].y 的边缘情况执行可选优化。这种优化可以安全地跳过,并且可以使解决方案的概念化和实施更简单。

对应算法的伪代码如下:

优化解决方案

如果V[1].y > V[n].y,那么V[1].y是最大值。

如果V[1].y < V[n].yV[n].y > V[n-1].y,那么V[n].y 是最大值。

如果V[1].y < V[n].yV[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) 案例。

【讨论】:

  • 如果情况 2,V[1] 和 V[2] 怎么都是局部最小值?
  • @Ank 好问题!我写的时候有点误会了。我的理由是,在V[1].y < V[2].y 的特殊情况下,对于任何i > 2,我们必须有V[i].y > V[2].y。相反,如果我们有V[i].y < V[2].y,则多边形不能是凸的。但是,在我的概念模型中,它们确实不会都是局部最小值。我会修正我的答案以反映这一点。
  • @Ank 我已经更新了我的答案以修复不准确之处。重要的一点是,当V[1] 是局部最小值时,二分搜索很容易,但如果不是,则很棘手。我想从简单的案例开始,然后与棘手的案例进行比较。我希望我的回答现在能更好地反映这一点。
  • @zaira 我无法理解 V[2] 如何在描述问题的约束条件下位于象限 2 内——即 V[1] 始终是最左边的顶点,并且顶点严格按逆时针顺序放置 - 防止在 V[1] 放置在原点时发生这种情况。在问题的约束下根本不可能。
  • @zaira 不用担心!很容易忽略这样的小细节,您花时间提出可能的错误是件好事。自从我发布此答案以来已经有几年了,所以无论如何,检查它以确保准确性是一个很好的练习:)
【解决方案2】:

您可以使用here 中所述的二分搜索找到任意方向的极值点。

基本思想是检查端点和中点的向量,并以此确定要扩展的部分。

【讨论】:

  • 我不明白链接中的描述。它是如何工作的?他们在 a 和 b 之间选择了一个顶点 c,并“假设”V[a,c] 和 V[c,b] 都是单调的。但是怎么保证??两个局部最大值都可以出现在 V[a,c] 或 V[c,b]
【解决方案3】:

因为多边形是凸的,所以连续点之间的向量角度从 270 度(向下。称为 -90 度)到 0(右)、90(上)、180(左)等单调增加,当你在多边形周围从一个点移动到另一个点时。

因此你可以通过二分搜索找到大于 180 度的最小角度。到下一个点的向量变为 > 180 度(在您的示例中为 V[8])的点是多边形从向上或向左转向向下的位置,并且该点必须具有最大的 Y 坐标。

我认为彼得链接到的那篇文章说了同样的话,但是对于这样一个简单的想法来说,要读很多话。

【讨论】:

  • 这个方法不错,但是如果我加入V[5]和V[1],现在就没有这个角度了。
【解决方案4】:

设 V[m] 为 y 坐标最大的顶点。

要考虑的最简单的情况是m=1,当V[2].y < V[1].y > v[n].y。由于排除这种情况使后续推理更简单,我们假设对这种情况进行了初始检查。

考虑一条以 V[i] 为原点的边 E[i],其中1<i<=n。鉴于所有 x 和 y 坐标都不同的约束,E[i] 必须位于 4 个平面象限之一:

鉴于我们已经排除了m=i=1 的情况,对于位于 I、II 或 IV 象限的 E[i],它必须是m>i 的情况。如果 E[i] 在象限 III 中,则要么是 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)]

【讨论】:

    猜你喜欢
    • 2014-03-13
    • 1970-01-01
    • 2020-03-20
    • 2016-07-28
    • 2011-05-15
    • 1970-01-01
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    相关资源
    最近更新 更多