【问题标题】:Plane sweep algorithm: How to order the segments after intersection point平面扫描算法:如何在交点后对线段进行排序
【发布时间】:2016-10-14 07:09:26
【问题描述】:

我正在尝试在 C++ 代码中实现基于本书的线段交叉点的平面扫描算法:http://www.cs.uu.nl/geobook/。他们建议使用平衡二叉搜索树来实现平面扫描的状态结构。

我正在使用 std::set 结构来实现状态结构,但是我在重新排序包含点“p”且它们的上端点是点“p”的段时遇到了问题。它们具有相同的坐标点,这意味着我不能将它们插入到 std::set 中,因为它不允许重复值......我试图用它们的下端点插入它们,但随后它们将失去顺序它们与“p”下方的一条扫掠线相交。

书中的伪代码如下:

  1. 将 U(p) ∪C(p) 中的段插入 Tao。 Tao中段的顺序应该对应于它们相交的顺序 p 正下方的扫描线。如果有一个水平段,它来了 在所有包含 p 的片段中排在最后。
  2. (∗ 删除并重新插入 C(p) 的段会颠倒它们的顺序。∗)

我不知道它们将如何颠倒它们的顺序。当我在状态结构中插入段时,我按它们的 x 上端点坐标对它们进行排序。我不知道如何在交叉路口后交换他们的订单。

有什么想法吗?

更新:这本书在这里:https://books.google.com/books?id=C8zaAWuOIOcC 但有一些页面没有出现。它在第 2 章,第 24、25 和 26 页。希望它有助于提供一些上下文

最好的,

【问题讨论】:

    标签: c++ algorithm computational-geometry


    【解决方案1】:

    当使用std::set 时,如果您使用std::set 的合适比较器,则公共y 值 上出现两个或多个项目应该不是问题。除了 y 值,我建议按斜率进行比较和排序。 (std::set 的比较器示例)

    我建议不要使用 std::set,而是使用 std::vector 之类的东西。使用 std::vector 可以交换(std::swap)对某些线段的引用,并且如果线段开始/结束,还可以在 O(log n) 时间内插入/删除,其中 n 是线段的数量。这个想法是通过交换与交叉点对应的线段,您自己在整个线扫描过程中保持状态的正确顺序,只有事件队列是优先级队列。 (由于@Sneftel 的评论而被删除,感谢您的洞察力。)

    关于您对扫描线算法的一般方法: 状态 (sweep line status) 确实代表了线扫描中特定(当前)时间线段的顺序。对于扫描线状态,据我了解,应该使用平衡二叉树(如@Sneftel 所述)。然后,您可以通过交换与交叉点对应的线段,在整个线扫描过程中自己保持状态的正确顺序,只有event queue 必须是某种优先级队列。

    【讨论】:

    • std::vector的问题在于中间插入/删除元素是O(n)而不是O(log(n))。这就是算法指定平衡二叉树的原因:它需要高效的搜索高效的中间插入/删除。尽管如此,对于合理的输入大小和类型,线性时间插入/删除不太可能成为扫描线实现中的主要性能问题。
    • 要实现平面扫描,您需要在 x 中创建一个“seg start”和“seg stop”事件列表。然后你对它们进行排序。然后创建一个动态有序列表,主要排序在 y 上,并将段插入其中。因此,即使 y 中有大量重叠,该算法仍然相当有效。问题是如果两个段共享一个 y 会发生什么。
    • @Sneftel,真的,std::vector 在这种情况下不起作用,附加线段随时开始。
    • 如果您首先按角度对随时开始的线段进行排序,然后按顺序插入它们,则它可以工作。
    【解决方案2】:

    用作平面扫描状态数据结构的std::set的排序谓词必须如下工作:

    1. 它必须(动态地)计算给定线段与扫描线当前位置的交点坐标。

    2. 在平局的情况下(当两条线段似乎在同一坐标处与扫描线相交时)它还必须比较两条线段的角度 - 这将允许找出线段在扫描线未来位置的状态。

    请注意,上面的要求 1. 意味着谓词对象必须持有对扫描线位置变量的引用,以便它可以在扫描线前进时正确比较段。扫描线位置不能存储在谓词本身中,因为这样您将无法从您的算法中更新它(std::set 不提供通过引用访问其谓词)。

    编辑

    请注意,维护集合中段的正确顺序(即根据需要交换它们)仍然由算法负责 - 带有此类谓词的 std::set 不会自动对其元素重新排序。

    【讨论】:

    • 这不是一个好方法。 std::set 需要一个不会随时间改变其输出的比较器,违反此导致错误。仅仅因为std::set使用平衡二叉树实现的,并不意味着它可以用作原始平衡二叉树;扫描线算法使用它的方式需要手动交换而不是排序函数,并且std::set 没有为此设置。
    • @Sneftel 我从来没有说过std:set 会自动保持状态中段的正确顺序。在处理交叉路口事件时,您仍然需要交换路段。
    • 这不仅仅是保持正确的顺序。 std::set 预计其比较功能不会改变。如果是这样,您的程序很可能会崩溃。
    • @Sneftel 如果比较函数的变化不影响集合中元素的当前顺序,为什么程序会崩溃?
    • 因为它确实会影响订单(否则没有理由进行比较)。没有办法在 std::set 中“交换元素”。
    【解决方案3】:

    编写比较函数,使主要排序在 y 上,次要在 x 上。然后,您可以插入具有重复 y 值的段,只要 x 不同。 (如果您有两个相同的段,则无论如何都需要专门处理交叉测试)。

    【讨论】:

    • 是的,问题是当你有多个段具有相同的上点(相同的x和y坐标)时,你如何在状态结构中对它们进行排序?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-22
    • 1970-01-01
    • 1970-01-01
    • 2015-03-07
    • 2015-11-26
    • 2020-02-06
    • 2018-06-30
    相关资源
    最近更新 更多