【问题标题】:Best dynamic data structure for 2d circle nearest neighbor二维圆最近邻的最佳动态数据结构
【发布时间】:2017-04-18 21:18:51
【问题描述】:

标题是最大的问题。我有一组圆,每个圆都由中心 C 和半径 r 给出。两个圆之间的距离是它们的中心之间的欧几里得距离减去它们的半径。对于圆 a 和 b,

d_ab = |C_a - C_b| - r_a - r_b。

请注意,如果圆圈重叠,这可能是负数。

那么在集合中找到给定圆的最近(最小距离)邻居的最快数据结构是什么?

必须支持通过以任意顺序交错的“查找最近”查询来添加和删除圆圈。事先对集合的几何分布一无所知。

这将是一个系统的核心,其中一个典型的圈数是 50,000 个,并且需要 10 的数千个查询、插入和删除,理想情况下在高端的用户交互速度(一秒或更少)平板设备。

最近的邻居已经被研究死了,但是这个带有圆圈的版本似乎有些困难。

我看过 kd-trees、quad-trees、r-trees 以及它们的一些变体。关于其中哪一个可能是最好的尝试的建议以及新的建议都会有很大的帮助。

【问题讨论】:

  • @NiklasB 对。这就是为什么我说“请注意,如果圆圈重叠,这可能是负面的。”在这个应用程序中是有意义的。如果有帮助,您可以假设存在一个固定常数,当将其添加到所有距离时,会使它们成为非负数。
  • 我一定是太笨了,看不懂,没关系
  • 中心是二维点还是一维点?
  • 您可以将 Voronoi 图用于您的圆圈集。它允许非常快速的搜索。但是更新可能会很慢,尤其是当您在许多小圆圈上方添加/删除大圆圈时。如果将圆分成 sqrt(n)...log(n) 子集(每个子集 - 用于某些不同大小的圆)并为每个子集维护单独的 Voronoi 图,这可能会得到改善。
  • @NiklasB。不,我是说在 50,000 个数据库中必须在一秒钟内运行 10 万次插入、删除和查询。

标签: algorithm language-agnostic computational-geometry


【解决方案1】:

Cover trees 是邻近结构的另一种可能性。它们不支持删除(?),但您可以在后台软删除和重建以防止垃圾堆积,这对于其他结构可能是一种有用的技术。

从 2D 圆问题减少到 3D 点问题,使用了这样的时髦度量。 (您命名的邻近结构应该是可调整的。)将一个以 (x, y) 为中心、半径为 r 的圆映射到点 (x, y, r)。将向量 (dx, dy, dz) 的长度定义为 sqrt(dx**2 + dy**2) + abs(dz)。这引发了一个度量。要找到最接近中心 (x, y) 的圆(查询圆的半径不相关),请在 (x, y, R) 处进行邻近搜索,其中 R 大于或等于一个圆圈(可以修改您的邻近结构,以便不必跟踪 R)。

根据我在点上实现 kd-trees 和 Voronoi 图的经验,从头开始实现 kd-trees 会容易得多。即使你重用了其他人的健壮几何图元(如果你走这条路,请这样做,以保持你的理智),Voronoi/点位置的退化边缘情况需要时间才能正确。

【讨论】:

  • 这是一个很好的答案:时髦的指标是我没有考虑过的。我已经从这个问题转移了一段时间。必须弄清楚我是否会接受这个或提供赏金以获得更多关注。不管怎样,我很欣赏你的想法。
  • 我认为您有一个符号错误。距离是 sqrt(dx^2 + dy^2) - abs(dz)。请注意,abs(dz) 被减去,而不是被添加。
  • @ScottSmedley 这不是一个指标。我们通过查询具有大 z 坐标的点的近邻来使大圆圈更接近。
  • 嗨@DavidEisenstat,我错过了什么?如果我的树有 2 个圆: p1 @ (0,0) 与 r=8 和 p2 @ (10,0) 与 r=1 并且我们正在搜索最接近点 (7,0) 的圆...添加abs(dz) 将导致算法选择 p2 作为最近的圆。而由于半径较大,p1 实际上更接近。
  • @ScottSmedley Circle 1 有坐标 (0,0,8)。圆 2 的坐标为 (10,0,1)。查询点的坐标为 (7,0,8),其中 8 来自圆半径的最大值。到 1 的距离是 sqrt((7-0)^2 + (0-0)^2) + |8-8| = 7. 到 2 的距离是 sqrt((7-10)^2 + (0-0)^2) + |8-1| = 10.
【解决方案2】:

我建议使用 KD-Tree 或其他允许 O(log N) 最近邻居的启发式算法。而不是使用单个点和半径来表示一个圆。在圆本身上使用 k 个等距点,加上圆的中心,否则您可能会遇到大圆内的小圆的问题。这与使用具有 k 个顶点的正多边形来表示一个圆的想法相同。然后可以取一个顶点并找到它的最近邻居(忽略同一个圆上的顶点),以根据最近的正多边形找到最近的圆的近似值。

表演如下:

创建 KD 树:O(kN log kN)

在 KD-Tree 中移除/添加圆:O(k log kN) -在KD-Tree中添加或删除一个圆的所有k点

最近圆查询(Circle):O(k log kN) - 这是通过首先删除圆的所有 k 点 (O(k log kN)) 来完成的,因为发现圆的最近邻居是圆本身并不是非常有用。然后对于圆中的每个 k 点,找到最近的邻居 (O(k log kN))。一旦找到最近的邻居,实际最近的(在一定误差范围内)是距离最小的那个(根据点和半径计算真实距离后)(O(1))。

如果您希望它更快,我建议使用 k = log(N);如果您希望它准确,我建议使用 k = sqrt(N)。

另外,我可能没有考虑到一些会导致问题的特殊情况,因此请注意它们。

【讨论】:

  • 确实如此,看来我忽略了那个。我在看扫描线算法。我将删除答案的那部分。不过,我会留下答案,因为它可能仍然可以用作近似值(也许对于使用更大数据集看到这个问题的其他人)。
  • 我的意思是O(n^(1/2) log n) :)
  • 我认为你之前的 O(N^(3/2)*log N) 值对于构建数据结构是正确的,O(N^(1/2) *记录 N) 查询。该算法在 k=sqrt(N) 的情况下具有相同的大 O,但在 k=log(N) 的情况下以准确性为代价具有更好的运行时间(对于大圆或高度聚集的圆更是如此)。跨度>
  • 建造时间是O(n * log n),但我的意思是查询/更新时间。
  • 再一次,我似乎略过了一些细节。还有查询时间。再次查看差异后,似乎唯一的主要好处是实际的最近邻操作,如果 k=O(log N),则可以在 O(log(N) * log N) 中完成,而不是 O(sqrt(N) * 日志 N)。使用另一种方法的插入和删除可以在 O(log N) 中完成,代价是 O(N) 空间,方法是通过映射或字典跟踪每个圆插入到哪个图表中。而这个最近的邻居/插入/删除都是 O(k*log(N))。仍然有人会觉得它很有用。
【解决方案3】:

如果保证圆没有大半径,至少最大半径 (R) 明显小于圆所在的区域,我认为它可以用标准空间分区和最近邻搜索来覆盖.

在与给定圆的距离最小的集合中搜索圆时,给定圆的半径无关紧要(距离定义)。因此,如果仅将中心(点)与圆集进行比较,则它是相同的.

这样,仅通过中心(点集)将圆集存储在空间分区结构中就足够了。圆的添加和删除以点的标准方式完成。找到离给定点最近的圆可以分两步完成:

  • 找到离给定点 P 最近的中心。说圆 C,圆心为 c,半径为 r。
  • 距离 P 较近的圆心只能在 P 周围,内半径为 r,外半径为 R-d(P,c)。搜索与该环相交的分区以查找候选者就足够了。

可以通过结合这两个步骤来优化搜索。在第一步中,一些感兴趣的分区已经被访问过。通过存储访问过哪些分区,并在这些分区中找到最小距离的圆圈,可以减少第二步的搜索。

【讨论】:

    【解决方案4】:

    感谢 @David Eisenstadt 提出 3d 搜索结构的想法。这是最佳答案的一部分,尽管不需要他奇怪的指标。

    关键是要详细了解最近邻搜索的工作原理。我将展示这个用于四边形。 k=3 的 Kd 树是相似的。这是伪代码:

    # Let nearest_info be a record containing the current nearest neighbor (or nil 
    # if none yet) and the distance from point to that nearest neighbor.
    def find_nearest_neighbor(node, target, nearest_info)
      if node is leaf
        update nearest_info using target and the points found in this leaf
      else
        for each subdivision S of node
          if S contains any point P where dist(P,T) < nearest_info.distance,
            find_neareast(S, target, nearest_info)
          end
        end
      end
    end
    

    完成后,nearest_info 包含最近的邻居及其距离。

    密钥是if S contains any point P where dist(P,T) &lt; nearest_info.distance。在 3d 空间中,在描述圆圈的 (x,y,r) 三元组中,我们有

    def dist(P,T)
      return sqrt( (P.x - T.x)^2 + (P.y - T.y)^2 ) - P.r - T.r 
    end
    

    这里 P 是八叉树长方体的八分圆中的任意点。如何考虑长方体中的所有点?请注意,对于给定的搜索,T 的所有组件都有效地固定,因此如果我们将目标写为常数点(a, b, c) 会更清楚:

    def dist(P)
      return sqrt( (P.x - a)^2 + (P.y - b)^2 ) - P.r
    end
    

    我们完全省略了c = T.r,因为它可以在算法完成后从最小距离中减去。也就是说,目标的半径不会影响结果。

    很容易看出P 我们需要获得长方体的最小距离是欧几里得关于x 和y 最接近目标的欧几里得,并具有最大表示半径。计算起来非常简单快捷:2d 点矩形距离和 1d max 运算。

    事后看来,这一切都是显而易见的,但需要一段时间才能从正确的角度看待它。感谢您的想法。

    【讨论】:

      猜你喜欢
      • 2015-11-16
      • 1970-01-01
      • 2011-10-21
      • 2018-02-26
      • 2011-08-10
      • 1970-01-01
      • 2020-12-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多