如何在O(nlogn)中找到相交线的上包络线?

时间:2011-09-14 17:06:18

标签: algorithm geometry computational-geometry analysis

免责声明:是的,这是一个家庭作业,我想了几天但却找不到办法。

所以有n条直线(y = ax + b),我想找到它们的上部包络线(图中的粗体部分)。它必须在O(nlogn)。

我理解的是,我需要找到一种方法来忽略某些行,因为如果我搜索所有行,它将不是O(nlogn)。

我正在思考一个分歧&征服方法,以便我可以将列表分成两个并递归继续直到解决方案。但后来我不知道如何摆脱一些线路。很明显,我不需要考虑图片中的一些底线,因为他们不可能为解决方案做出贡献。但是我没有想到任何事情。任何提示都表示赞赏。

enter image description here

8 个答案:

答案 0 :(得分:6)

提示:此问题基本上是convex hull问题的双重问题。

解决方案:如果您将每一行y=ax+b视为一个点(a,b),并在(0, -infinity)添加一个额外的“点”,您应该能够将其输入任何2D凸包算法并得到正确的解决方案。

注意:相反,此问题的任何解决方案也可用于构建相同O()的2D凸包算法。


编辑:证明它的请求......

如果某个点(x,y)位于特定行y=ax+b之上,则会出现不等式ax - y + b > 0

这个不等式也等同于(-a,b)在线(b)=x(-a)+y之上,其中x是斜率,y是截距。

这个duality允许我们将点作为线和线处理为点:点和线上的任何证明或算法都可以映射到一个同样有效的点,线和点反转。

在这种情况下:一组2D点的凸包确定了不是其他点的凸组合的“极端”点,以及连续极值点之间的线。相应地,凸包的双重版本确定那些不是其他凸起组合的“极端”线,以及连续极端线之间的交叉点。这是给定行集的包络。

凸包的双重性为您提供输入线组的上下包络。由于您只需要线条的上部包络线,因此您需要添加一条低于任何可能的常规线条的线条以消除下部线圈。或者,您可以查看解决方案并仅选择坡度增加的交叉点。

相反,该问题的任何解决方案都可用于确定一组点的上凸壳。运行两次,一次为行{(a,b)},再为行{(-a,-b)},将为您提供一个完整的凸包。

答案 1 :(得分:2)

首先,我们为这些行构建两个不同的二元搜索树,一个根据a排序,另一个根据b排序。

现在我们开始考虑行lminlmax,这些行是最小和最大a的行;他们将确保从第二小和第二大线的交叉点给出的点数,我们将这两个点添加到上面的信封。

现在我们考虑(xi,yi)lmin之间的交叉点lmax,我们理想地绘制垂直x = xi行。我们现在要确定在坐标x = xi中与y0 s.t. y0 <= yi相交的线,并从树中删除所有这些线。

我们如何识别这些线?我们使用第二个树找到所有带b s.t. lmin <= b <= lmax的行。

最后,我们还会从树中删除lminlmax

现在我们将对所获得的新树进行递归。

答案 2 :(得分:1)

如果我看对了,那么这些行总是按照“a”值的顺序对“包络”做出贡献。所以按它排序。如果你有两个相同的a,它们是平行的,b决定哪个在另一个之上(你可以省略下面的)。如果知道线的顺序,则可以计算O(1)中两条成功线的交点。所以基本上它只不过是排序,那就是O(n log n)。

编辑:好的,其中一条评论是正确的 - 没有平行线没有分配到信封 - 原因是它们会对拐点以外的信封做出贡献。但是信封段是按照“a”的顺序排列的事实仍然是正确的(这意味着你总是有开始和结束段)。

问题是如何确定哪一行对信封有贡献,哪些不对。 您在阵列上扫描一次以找到转折点(必须是“a”切换符号的位置)。你从那里开始下降(减少a)和一次上升(增加a)。您计算与下一行的交点 - 如果它在错误的一侧(不减少/增加)x,则跳过它。扫描以移除平行线(具有相等的a),您仍应在排序后应用,因为这在计算交叉点时省略了病理情况。

答案 3 :(得分:1)

这是对Simones答案的评论,我认为这是不正确的。

  

现在我们开始考虑线条lmin,lmax,它们是线条   最小的和最大的;他们将为此做出贡献   从与第二小的交叉点和交叉点给出的点   第二大线,我们将这两点添加到鞋面   信封。

不一定非必须如此。有助于信封的部分也可以是从lmin到列表中任何其他行的部分。例如:

enter image description here

此外,在x = xi处排除y&lt; = yi的所有行似乎是合理的。但是这些行没有通过b_lmin和b_lmax之间的值b来识别(如果这是你的意思,它有点不清楚)。一个反例,是:

enter image description here

我希望我没有误解你的算法描述。如果有,请告诉我!

答案 4 :(得分:0)

我不知道如何解决这个问题,但请注意你确实知道最左边和最右边的线(因为x倾向于减去加上无穷大),因为那些将具有最小和最大值a (因为x变大ax支配b}的任何值。

鉴于此,您可以找到它们相交的位置并丢弃该点下方的线(我认为)。然后你可以以某种方式迭代。 (例如,通过在交叉点的x坐标处找到最高线,然后用与原始两条线相交的两个点重复...)。希望有所帮助。

答案 5 :(得分:0)

这是输出敏感算法:

for t = 0, 1, 2 ... do
     k = 2^(2^t)
     arbitrarily partition the segments into ceiling(n/k) subsets each of size at most k
     run any O(nlogn) time algorithm on each group yielding ceiling(n/k) monotone polygonal chains
     find the upper envelope of these monotone polygonal chains, and abort if the output size exceeds k
end for

运行时间:O(nlogk),其中k =答案中的段数。 这本质上是Chan的凸壳算法的双重思想

答案 6 :(得分:0)

我知道这个问题很陈旧,但我并不同意所有的论点,特别是接受答案中的论点。

一些直截了当的论证似乎无法解决这个问题。事实上,事实证明,大多数分而治之的算法只能在O(n log n a(n))中实现运行时间,包含逆Ackerman函数a(n)。

然而,有一篇论文提供了一个很好但不是无关紧要的解决方案: http://www.sciencedirect.com/science/article/pii/0020019089901361

请注意,该算法是针对有限线段设计的。但是,很容易为最小和最大可能的交叉点提供界限并且转换&#39;仿射函数在这些区间内对线段起作用。

答案 7 :(得分:-1)

我想象从(0,+ INFINITY)发送到我们的行集的光线。射线的第一个碰撞点是我们信封的一部分。在任何3个连续的碰撞不是共线的情况下,在碰撞点1和2以及2和3之间发送更多的射线。对于击中同一线的连续碰撞,只需要在输出组中记录一个。然后,您将使用碰撞线组生成每对线之间的实际顶点。

不幸的是,这会给出一个很好的估计,但不是一个确切的答案(?因为你需要无限多的光线?)。

Step1)投射你的第一批光线{0,-Pi / 4,-3Pi / 4,-Pi}

R | L
1 | Line8
2 | Line2
3 | Line2
4 | Line1

步骤2)在唯一线(1和2,以及3和4)的连续命中之间投射光线。插入结果并剔除内部重复(两侧都有相同线的线命中)。

R | L
1 | Line8
5 |  Line8 * culled out
6 |  Line8
7 |  Line5
8 |  Line2
2 | Line2 * culled out
3 | Line2 * culled out
9 |  Line2 * culled out
10|  Line2
11|  Line1
12|  Line1 * culled out
4 | Line1

步骤3)重复步骤2直到(??魔法测量精度)。

步骤4)通过在结果中的所有连续唯一线之间进行交叉来生成包络点列表。