【问题标题】:Faster way to compare two sets of points in N-dimensional space?在 N 维空间中比较两组点的更快方法?
【发布时间】:2011-02-24 00:50:26
【问题描述】:

List1 包含大量 (~7^10) 个 N 维点 (N List2 包含相同或更少数量的 N-维点(N

我的任务是:对于 List1 中的每个点,我想检查 List2 中的哪个点最接近(欧几里德距离)List1 中的一个点,然后对其执行一些操作。我一直在做简单的嵌套循环方式,当我在 List1 中没有超过 50 个点时,但是有 7^10 个点,这显然会占用很多时间。

最快的方法是什么?计算几何中的任何概念可能会有所帮助?

编辑:我有以下内容,我已经从 List2 构建了一个 kd-tree,然后现在我正在对 each 点进行最近邻搜索在 List1 中。现在,正如我最初指出的那样,List1 有 7^10 个点,因此虽然我节省了每对的蛮力、欧几里德距离方法,但 List1 中的点数非常多 导致大量时间消耗。有什么办法可以改善吗?

【问题讨论】:

  • 您能否分析您的代码以查看大部分时间都花在了哪里(以及是否)?也就是说,如果 80% 的时间都在做拆分,可以尝试简化拆分代码。如果 80% 正在做距离比较,您可以尝试改进比较(例如:如果一个轴上的距离大于您当前的最小总距离,则不要费心检查其他维度)等等。让我们看看在哪里进行优化。

标签: algorithm math geometry computational-geometry euclidean-distance


【解决方案1】:

如果不了解两种解决方案中点的分布,就不可能告诉您哪种算法最有效。但是,第一次猜测......

第一个算法不起作用 - 有两个原因:(1) 错误假设 - 我假设边界外壳是不相交的,(2) 对问题的误读 - 它没有找到每对点的最短边。

...计算两个集合的凸包:最近的点必须在两个包的超面上,两个重心之间的线通过该超面上。

您可以通过计算中心点来计算凸包,假设所有点具有相同的质量,重心,然后将列表从离中心最远到最远排序。然后取出列表中最远的点,将其添加到凸包中,然后删除到目前为止计算的凸包内的所有点(您需要计算大量 10d 超三角形来执行此操作)。重复直到列表中没有任何东西不在凸包上。

第二种算法:部分

计算 List2 的凸包。对于 List1 的每个点,如果该点在凸包之外,则按照第一种算法找到超面:最近的点必须在该面上。如果是在脸上,同样如此。如果它在里面,你仍然可以通过将线延伸到 List1 的点来找到超面:最近的点必须在包含超面到 List2 重心的球内:但是,在这里,你需要一个新的算法来获得最近的点,也许是 kd-tree 方法。

性能

当 List2 是均匀分布,或正态分布时,通过一些相当倾斜的形状,这将很好地减少考虑的点数,并且应该与 kd-tree 建议兼容。

虽然有一些可怕的麦芽汁情况:如果 List2 仅包含圆环表面上的点,其几何中心是列表的重心,那么凸包的计算将非常昂贵,并且无济于事大大减少了考虑的点数。

我的评价

这些几何技术可能是对其他海报的 kd-trees 方法的有用补充,但您需要先了解一点点的分布,然后才能确定它们是否值得应用。

【讨论】:

  • 即使问题是要找到整体上最接近的点对(实际上不是——他实际上希望 L1 中的 每个 点都有最接近的伙伴),最近的一对不必在任何一个船体上。 L1 中心附近的某个点可能比 L1 的任何船体点到 L2 的任何船体点更接近 L2 中心附近的某个点。
  • @j_random:我已经编辑了帖子以反映您的评论,并描述了修改后的算法。
  • Charles:很抱歉,我不太了解计算几何,因此凸包方法可能不是我可以快速使用的方法。非常感谢您的回答。
  • 感谢更新。但是... :) 如果船体的体积是不相交的,那么离 L1 船体上任意点最近的点必须在 L2 船体上的某个位置,但该点可能位于沿线段的某个位置,而不是位于由L2 的实际点。 (恐怕更容易在图表上显示这一点。)还不相信穿过质心的线具有您所赋予的属性(尽管它可能是一个很好的启发式)-您可以将质心移动到任何地方通过在边界附近添加许多点。 (抱歉挑剔了...... :))
  • @j_random “离 L1 船体上任何一点最近的点必须在 L2 船体的某个地方”——我真的这么说吗!实际上,即使您说的也不一定是真的:最近的点可能就在船体内部。但它必须在该超面的最小边界球内。第二个属性是正确的,即使 L1 和 L2 是平行的牌坊:在这种情况下,球可能非常大!
【解决方案2】:

一个好方法是使用 kd-tree 之类的东西并执行最近邻搜索。幸运的是,您不必自己实现此数据结构,之前已经完成。我推荐这个,但还有其他的:

http://www.cs.umd.edu/~mount/ANN/

【讨论】:

  • 由于我现有的代码是在 Python 中的,我不会使用这个库。我希望使用gamera.sourceforge.net/doc/html/kdtree.html。但是,最重要的是使用 kd-trees 的概念。谢谢!
  • PeterK:抱歉切换了“接受”。您现在可以看一下已编辑的问题吗?
  • 我认为我们的选择已经不多了。我认为有两种可能性:首先,您可以在搜索 NN 时使用某种近似值。 Mentoined ANN 库允许这样做,但不知道其他库。第二种选择是以某种方式预处理 List1 - 比如计算某种大小的“集群”。集群是指非常相似的点,因此可以选择其中一个作为集群的代表。如果您随后对多个邻居运行 NN 搜索,您可能会得到很好的结果(但这仍然取决于您到底想要做什么)。
  • 哦,还有一件事:您可以采用并行方式,一次进行更多搜索。当然,这只有在您拥有多核处理器并且您的系统允许线程处理时才合理。此外,您需要 kd-tree 是线程安全的,至少在搜索方面是这样。
【解决方案3】:

kd-tree 非常快。我已经使用了本文中的算法,并且效果很好Bentley - K-d trees for semidynamic point sets

我确信周围有图书馆,但有时很高兴知道发生了什么 - Bentley 很好地解释了这一点。

基本上,有多种搜索树的方法:最近的 N 个邻居、给定半径内的所有邻居、半径内的最近 N 个邻居。有时您想搜索有界对象。

这个想法是 kdTree 递归地划分空间。每个节点在您所在空间的一个维度中沿轴拆分为 2。理想情况下,它垂直于节点的最长维度拆分。您应该继续拆分空间,直到每个桶中有大约 4 个点。

然后对于每个查询点,当您递归访问节点时,您会检查您所在的特定节点到隔墙的距离。如果到的距离为隔墙比搜索半径更近。如果墙超出半径,只需搜索您所在节点的子节点。

当您到达一个桶(叶节点)时,您可以测试其中的点以查看它们是否在半径范围内。

如果你想要最近的点,你可以从一个很大的半径开始,并在递归时传递一个指针或对它的引用——这样你可以在找到接近点时缩小搜索半径——然后回到最近的点很快。

【讨论】:

  • 实际上,你并不需要根据最长的维度进行拆分,一种非常有效的技术是在每个维度上交替拆分,始终以相同的顺序进行。这可能不是最有效的一种,但它是可预测的,而且这也有一些优势。
  • 谢谢!我想我会试试的。我想它会加快构建速度,因为找出最长的维度可能很昂贵? - 即使在 Bentley 的论文中进行了优化 - 即(使用点的子集进行测量)。
  • 找出哪个维度的价差最大真的很昂贵。引入近似值(检查点的子集)会给您带来相当大的加速(当然取决于子集的大小)。根据我的经验,这是完全值得的。
【解决方案4】:

(一年后)在查看所有 200M 点中的 1M 之后,提前退出的 kd 树, 在高维度上可以快得多
结果仅在统计上接近绝对最接近的值,具体取决于数据和指标; 没有免费的午餐。
(请注意,采样 1M 点,而 kd 树仅采样 1M,是完全不同的,更糟。)

FLANN 对 dim=128 的图像数据执行此操作, 我相信opencv吗?快速可靠的本地模组 SciPy cKDTree 也有 cutoff= 。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    • 2015-07-04
    • 2020-09-10
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多