多边形内部嵌套多边形问题

14

我有一些简单多边形,它们之间不相交,但某些多边形可能嵌套在其他多边形中。

例如:

+--------------------------------------------+
|                                            |
|   +----------------+          +--------+   |
|   |                |         /         |   |
|   |   +--------+   |        /          |   |
|   |   |        |   |       /  +----(2)-+   |
|   |   |        |   |      /   |            |
|   |   +----(3)-+   |     /    |   +---+    |
|   |                |    +-----+   |   |    |
|   +------------(2)-+              +(2)+    |
|                                            |
+----------------------------------------(1)-+

如何找到所有多边形的“深度”?换句话说,如何找出一个多边形被多少个多边形包含? “深度”是括号中的数字。

我可以计算一个多边形的一个点在所有其他多边形内部的次数,但这样做具有二次复杂度。如何更快地计算这些深度?


[[v1, v2, v3], [v4, v5, v6, v7], [v8, v9, ... - 基本上是一个平面列表,没有任何树状结构,如果这就是你的意思。 - Ecir Hana
1
@EcirHana,这个问题可以参考《多边形套嵌和鲁棒性》一文中的处理方法。如果没有其他人回答,我稍后会提供答案。 - mmgp
1
如果您需要知道给定多边形P的深度,并且您有一个保证在每个多边形外部的点O,也许通过计算线段O和距离O最近的P顶点之间唯一相交的多边形数量可能起作用。 - didierc
@MC:我指的是任何类型的简单多边形。 - Ecir Hana
@SamuelAudet,你能否向我解释一下如何使用线性扫描将时间复杂度降至O(nlogn)? - Ecir Hana
显示剩余10条评论
5个回答

2
将多边形放入某种空间查询结构中,例如基于最小边界矩形的R树。然后,您应该能够以O(n log n)的时间计算出所需的包含关系。(除非您处于一种病态情况,其中许多多边形的最小边界矩形重叠,但根据您的描述似乎不太可能发生这种情况。)
编辑后添加:当然,您不能仅依靠R树告诉您一个多边形是否在另一个多边形内部(它是基于最小边界矩形的,因此只能给出近似值)。您使用R树便宜地标识候选包含关系,然后以昂贵的方式验证它们(检查一个多边形中的点是否在另一个多边形内部)。

1
这是一种非常简单的方法,根本不需要病态案例就会失败。假设有一个形状像“C”的大多边形,如果我们在“C”的空闲区域放置一个正方形,为什么你会说这个正方形被其他多边形包含?或者也许这是病态案例,我正在对一个不属于我的问题添加不必要的要求。 - mmgp
谢谢你的回答,但我认为这不会起作用。问题在于构建查找结构所需的时间,因为它不会被再次重用(即每次多边形可能都不同)。此外,bboxes 可能重叠,也可能不重叠,这是 50%,而且很多 :)。 - Ecir Hana
构建R树的成本为O(nlogn),因此我认为这不是一个问题。 - Gareth Rees
@EcirHana 这个方法可行,建立空间索引可以非常快速,我建议使用 PMR 桶矩形四叉树作为空间索引。 - AlexWien

1
在您的示例中,您描绘了一个非凸多边形,因此其他人建议的rtree方法本身将不起作用。但是,每当您使用rtree方法找到匹配项时,您可以与附加检查相结合,以查看多边形的顶点是否在另一个多边形内部。这将减少您必须进行“点在多边形内”检查的次数。
另一种方法是采用您的第一个想法-即检查每个多边形的一个顶点与每个其他多边形,并修改它以缓存已经计算过的结果并重复使用它们,以减少时间复杂度。下面描述的方法本质上将多边形本身构建成树结构,而不是使用rtree。它实际上是一种不同的树结构,可以处理非凸多边形。
  1. 最初,每个多边形都是一个根节点,并且在自己的树中。
  2. 您必须循环遍历根节点集合,并使用“点在多边形内”测试检查它是否在另一个根节点内。
  3. 当找到匹配项时,从根节点集合中删除较小的多边形,并将其作为子节点添加到较大的多边形中。您开始构建一个有意义的树结构,并且还减小了您正在迭代的容器的大小(根节点),因此将来的迭代速度会更快。
  4. 继续这个过程,直到根节点的数量停止变化。
  5. 请记住,在主嵌套循环中,您只比较一个根节点是否在另一个根节点内。当找到匹配项(小节点在大节点内)时,可以跳过较小节点的子树。但是您必须遍历较大节点的子树,并进行检查以找到较小节点在层次结构中的正确位置。与简单的二次复杂度的嵌套循环相比,您仍然避免了很多检查。

0

这种方法与@GarethRees的想法类似:首先,廉价地发现哪些多边形对不需要检查包含关系。一旦需要检查的多边形对数可接受,就进行精确(昂贵)的几何检查。

很容易为每个多边形p计算其边界矩形,即左、右、上和下,因此让我们首先这样做。例如,对于左侧:p.L = min { x : (x,y)是p的顶点 }时间与点数成线性关系。

为了避免将每个多边形与其他每个多边形进行比较,可以将区域分成2x2的网格。假设区域的宽度和高度分别由DxDy给出,则可以创建九个集合top,bottom,left,right,topright,topleft,bottomright,bottomleft,rest并执行以下操作:

for p in polygons:
    in_top    = p.B > Dy/2
    in_bottom = p.T < Dy/2
    in_left   = p.R < Dx/2
    in_right  = p.L > Dx/2 
    if in_top:
        if in_left:
            add p to topleft
        elsif in_right:
            add p to topright
        else:
            add p to top
    elsif in_bottom:
        if in_left:
            add p to bottomleft
        elsif in_right:
            add p to bottomright
        else:
            add p to bottom

    if in_right and not (in_top or in_bottom):
        add p to right
    elsif in_left and not (in_top or in_bottom):
        add p to left

    if not (in_top or in_bottom or in_left or in_right):
        add p to rest

这又是线性时间。每个多边形都被分成了它最“紧密”包含的扇区。这样做有什么好处呢?比如说,你知道对于任何在left中的多边形p,它们与集合right之间不可能存在包含关系,因此你不需要进行比较。同样,在bottomleftrightbottomlefttopleft之间也是如此。下面是在你的示例上的效果:

                      Dx/2
+----------------------|---------------------+
|                      |                     |
|   +----------------+ |        +--------+   |
|   |                | |       /         |   |
|   |   +--------+   | |      /          |   |
|___|___|________|___|_|____ /__+===d(2)=+___|_ Dy/2
|   |   |        |   | |    /   |            |
|   |   +---b(3)-+   | |   /    |   +---+    |
|   |                | |  +-----+   |   |    |
|   +-----------c(2)-+ |            e(2)+    |
|                      |                     |
+----------------------|----------------a(1)-+

所以,rest = {a}, top = {}, bottom = {}, left = {b,c}, right = {d}

topleft = {}, topright = {}, bottomleft = {}, bottomright = {e}

因此,现在您需要使用昂贵的精确检查来比较最多bcde以及a与其他所有元素。实际上,如果您以聪明的方式对检查进行排序,则不需要将a与其他所有元素进行比较,因为包含是传递性的,因此如果您注意到c包含b,并且a包含c,那么就不需要检查a是否包含b

另一个要点是,您可以递归地应用上述推理。例如,假设集合topright超出了您的预期大小;那么您可以通过进一步划分该子区域来应用相同的技术(只需要正确进行簿记)。


感谢您提供详细的答案!不幸的是,我真的对那些“昂贵的精确”检查非常感兴趣。我知道我可以使用空间哈希、边界框或边界层次结构,但据我所知,它们都不能加速最坏情况。上面由@mmgp提到的论文“多边形嵌套和鲁棒性”似乎是最接近正确方法的,但将其转换为可工作的代码有点困难。 - Ecir Hana
在你开始之前,请问它们有什么复杂度? - Ecir Hana
@EcirHana:多边形1的顶点数乘以多边形2的顶点数。 - mitchus
除以二,没错。这就是我在问题中提出的建议,我希望能够改进它。 - Ecir Hana
据我所理解,@EcirHana提议检查每一对多边形,即在多边形数量方面呈二次增长。我建议首先通过简单的线性时间启发式方法消除许多这样的多边形对,然后对于我们没有排除的所有情况,进行精确检查,这在剩余多边形的顶点数量方面呈二次增长。如果您同意,请告诉我。 - mitchus
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/23580/discussion-between-ecir-hana-and-mitchus - Ecir Hana

0

在我看来,您可以通过将多边形排序并使用一个多边形是否在另一个多边形内作为比较运算符的测试来解决问题。

步骤1

假设我们定义多边形之间的关系'<'如下:A < B当且仅当A在B内部。 恰好发生的是,如果A < B且B < C,则A < C(即如果多边形A在B内部且B在C内部,则A必须在C内部)。现在,我们在任意多边形之间有一个严格的弱序关系。

[编辑:您需要使用某种点在非凸多边形中的测试,但是您可能已经在这样做了。]

步骤2

使用您喜欢的基于比较的排序算法根据此比较对多边形进行排序。例如,归并排序的最坏时间复杂度为O(nlogn),其中n是多边形的数量。

[编辑:这是重要的一步,因为它消除了二次复杂度。]

步骤3

确保“最大”的(即最外层的)元素位于多边形的排序数组中的第一位。(如果需要,请反转列表-它与多边形的数量成线性关系)。

步骤4

现在,“最大”的(即最外层的)多边形应该是第一个元素。

[编辑: 实际上,多边形已经按照它们的深度进行了排序。然而,两个具有相同深度的多边形可能会以不同的顺序出现,这取决于排序是否稳定。这对我们来说并不重要;我们感兴趣的是深度的变化。]

我们将按照以下方式为每个多边形分配深度。 首先,将每个多边形的深度初始化为0([编辑:根据示例初始化为1])。接下来,遍历您排序后的列表,但这次只将每个元素p与下一个元素p+1进行比较。如果(p+1 < p)为真,则增加p+1的深度。否则,将p+1的深度设置为与p相同的深度。


如果没有任何多边形是嵌套的呢?也就是说,如果所有的多边形都是“最外层”的呢? - Ecir Hana
那么在任何顺序下,它们都将被视为已排序,并且它们都具有深度为0的特点,因为每个比较 < 都将返回false。这不是你想要的吗? - maditya
另外,我刚刚意识到你的“最外层”深度应该是1(根据你的示例)。这可以通过在第4步中将深度初始化为1而不是0来实现。 - maditya
我想说的是,排序执行n log n比较,每个比较具有线性复杂度。因此它的时间复杂度为O(n n log n) - 比问题中的朴素版本更糟糕。 - Ecir Hana

0

步骤1:将多边形朝同一个方向定向,比如逆时针。

步骤2:对于任何需要计算“深度”的点(x,y),计算总绕组数。这可以通过多种方式完成;实践中最快的方法是计算水平(或垂直)射线与起始点(x,y)之间的交点的有符号数量。

特别地,每个多边形的深度将是其任何顶点的深度。


第二步具有线性复杂度,我必须为所有多边形重复执行它,因此它具有与上述原始方法相同的二次复杂度。 - Ecir Hana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接