【问题标题】:Nearest vertex search最近的顶点搜索
【发布时间】:2012-12-28 14:00:01
【问题描述】:

我正在寻找有效的算法来找到最接近点 P(x, y, z) 的顶点。顶点集是固定的,每个请求都带有新的点 P。我尝试了 kd-tree 和其他已知方法,但我到处都遇到同样的问题:如果 P 更接近则一切都很好,只对少数树节点执行搜索.但是,如果 P 足够远,那么应该扫描越来越多的节点,最终速度变得无法接受。在我的任务中,我无法指定一个小的搜索半径。这种情况有什么解决办法?

谢谢 伊戈尔

【问题讨论】:

  • k-d 树有多少个点?此外,k-d 树是这个问题的标准解决方案;我很惊讶他们对你来说不够快。您确定问题不在其他地方或不在 k-d 树实现中吗?
  • 点数是可变的(取决于用户的型号),通常很小,如 2-5K。但我有大量的查询(数百万)。
  • 示例:顶点在球体上,R = 100,查询点 P 在球体的中心。树划分点,到分隔符的距离为 100。需要检查两半。下一个分割-距离再次为100,也需要扫描两者。最后检查所有(或几乎所有)顶点。如果 P 更接近界限 - 事情会变得更好,但仍然检查了很多点。当然,如果 P 在附近的表面 - 树很快,但我也必须计算远点的距离
  • 啊,球形数据是 k-d 树的最坏情况输入。 :-)
  • 你能保证数据是球形的吗?另外,是所有测试点都靠近中心,还是只是其中一些?

标签: algorithm data-structures nearest-neighbor kdtree closest-points


【解决方案1】:

加快搜索速度的一种可能方法是将空间离散为大量以固定间隔隔开的矩形棱柱。例如,您可以将空间分成许多 1 × 1 × 1 单位的立方体。然后,您将空间中的点分布到这些体积中。这为您提供了一种“散列函数”,用于将点分布到包含它们的体积中。

完成此操作后,请执行快速预计算步骤,并为这些卷中的每一个找到最接近的非空卷。您可以通过检查距离该卷一步,然后两步等所有卷来做到这一点。

现在,要进行最近邻搜索,您可以执行以下操作。首先将空间中的点散列到包含它的卷。如果该卷包含任何点,则遍历所有点以找到最接近的点。然后,对于您在此过程的第一步中找到的每个卷,遍历这些点以查看它们是否更接近。生成的最近点是离您的测试点最近的邻居。

如果您的卷最终包含太多点,您可以通过将这些卷细分为更小的卷并重复相同的过程来改进此方法。您也可以创建一堆较小的 k-d 树,每个卷一个,以进行最近邻搜索。这样一来,每棵 k-d 树拥有的点数比您的原始 k-d 树要少得多,并且每个体积内的点都是最近邻的合理候选者。因此,搜索应该快得多。

这种设置在本质上类似于八叉树,不同之处在于您将空间划分为一堆更小的区域,而不是八个。

希望这会有所帮助!

【讨论】:

  • 好,我们细分成小方块。 (快速)寻找 P 所在的立方体。操作 - 没有这样的立方体。寻找 26 个相邻的立方体 - 没有找到。现在呢?
  • @Igors- 这个想法是将所有空间划分为立方体,而不仅仅是包含测试空间中的点的空间部分。这样,你总是会碰到一个立方体。至于击中 26 个相邻的立方体——假设你的立方体不是太大并且每个都包含一个小的 k-d 树,我的猜测是性能不会太差。
  • 用立方体填充整个体积可能会非常昂贵。即使是这样,如果找到一个空立方体怎么办?参考它的父级直到它包含一些点?它没有什么新东西(仍然应该扫描很多节点)。常规缓冲区(可能是 A 缓冲区)怎么样 - 但我不知道如何在这里构建它。想法?
  • @Igors- 我最初的想法是为每个立方体预先计算最近的非空立方体,这样如果你找到一个空立方体,你可以轻松并立即跳转到它的所有最近邻居。至于内存使用,这应该可以通过调整大小来调整。不过,我的感觉是你不喜欢这个主意。我以前没有听说过 A 缓冲区或常规缓冲区,所以你最好单独问一个关于它们的问题。
  • 你说得对,我不应该混合主题。我也意识到并不是每个问题都有一个简单的答案。谢谢你的帮助
【解决方案2】:

嗯,这不是所使用的索引结构的问题,而是您的查询的问题:

离数据集越远,最近的邻居就会变得越模糊。

所以我怀疑任何其他索引都会对您有很大帮助。

但是,您可以在搜索中插入阈值。 IE。 “找到最近的邻居,但只有在最大距离 x 内”。

对于静态、内存中的 3-d 点双向量数据,具有欧几里德距离,实际上很难击败 k-d-tree。它只是非常非常快速地拆分数据。八叉树有时可能更快,但我猜主要用于窗口查询。

现在,如果您确实只有很少的对象但有数百万个查询,您可以尝试做一些混合方法。大致是这样的:计算数据集凸包上的所有点。计算中心和半径。每当查询点距离 x 倍远(您需要自己进行 3d 数学运算以找出正确的 x)时,它的最近邻居必须是凸包点之一。然后再次使用 k-d-tree,但只包含船体点。

甚至更简单。找到每个维度中的最小/最大点。也许添加一些额外的极端(在 x+y、x-y、x+z、x-y、y+z、y-z 等)。所以你得到了一小部分候选人。所以现在让我们假设是 8 分。预先计算这 6 个点的中心和距离。设 m 为从中心到这 8 个点的最大距离。对于查询,计算到中心的距离。如果这大于 m,则首先计算这 6 个候选者中最接近的。然后查询k-d-tree,但将搜索绑定到这个距离。这会花费您 1(近距离)和 7(远邻居)距离计算,并且可以通过尽早提供好的候选者来显着加快搜索速度。为了进一步加快速度,也可以将这 6-26 个候选者组织在一个 k-d-tree 中,以便快速找到最佳边界。

【讨论】:

  • 哦,仔细检查并广泛地对您的代码进行基准测试。例如,您可以在没有 node 类型的情况下实现 k-d-tree。您还可以保存除 1 平方根以外的所有计算!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-21
  • 2011-10-21
  • 1970-01-01
  • 2021-11-19
  • 2018-06-16
  • 1970-01-01
  • 2016-06-23
相关资源
最近更新 更多