最近点算法

时间:2011-09-03 11:53:28

标签: ruby algorithm location

我有一个~5000点的列表(指定为经度/纬度对),我想找到用户指定的最近的5点到另一点。

有人能建议一个有效的算法来解决这个问题吗?我在Ruby中实现了这个,所以如果有一个合适的库那么就可以了解,但我仍然对算法感兴趣!

更新:有几个人要求提供有关此问题的更多具体细节。所以这里:

  • 5000点大多在同一个城市内。它外面可能有一些,但可以安全地假设它们中有99%位于半径75公里范围内,并且所有这些都在半径200公里范围内。
  • 点数列表很少变化。为了争论,让我们说它每天更新一次,我们必须在那个时候处理几千个请求。

7 个答案:

答案 0 :(得分:5)

您可以通过使用quad-treekd-tree对2D空间进行分区来加速搜索,然后在到达叶节点后,逐个比较剩余距离,直到找到最近的距离为止匹配。

另请参阅this blog post,其中引用了this other blog post,它们都讨论了在Ruby中用kd-tree搜索最近邻居的搜索。

答案 1 :(得分:3)

你可以使用曼哈顿距离(按纬度缩放)获得一个非常快速上限估计距离,这应该足以拒绝99.9%的候选人(如果他们不接近)(EDIT :从那以后你告诉我们他们很接近。在这种情况下,你的指标应该是距离平方,根据Lars H评论)。 考虑这相当于拒绝球形矩形边界框外的任何东西(作为圆形边界框的近似值)。 我不做Ruby所以这里是伪代码算法:

参考点P(pa,po)的纬度,经度其他点X(xa,xo)。 预计算 ka ,纵向距离的纬度比例因子: ka(= cos(pa in°))。 (严格地说,ka =常数是P附近的线性化近似。)

然后距离估算器为: D(X,P) = ka*|xa-pa| + |xo-po| = ka*da + do

其中| z |表示abs(z)。在最坏的情况下,这会高估真实距离√2(当da == do时),因此我们允许如下:

进行正在进行的搜索并保持Dmin,这是第五小的缩放曼哈顿距离估计值。 因此,你可以预先拒绝所有D(X,P)&gt;的点。 √2* Dmin (因为它们必须至少比√((ka * da)²+do²更远) - 这应该消除99.9%的点数)。 保留所有剩余候选点的列表,其中D(X,P)<=√2* Dmin。如果找到新的第五小的D.优先级队列,则更新Dmin,否则列表(coord,D)是良好的数据结构。 请注意,我们从未计算过欧几里德距离,我们只使用了浮点乘法和加法。

(考虑这类似于四叉树,除了过滤除了我们感兴趣的区域之外的所有内容,因此无需预先计算准确的距离或构建数据结构。)

如果你告诉我们纬度,经度(度,分钟或者什么?)的预期传播会有所帮助?如果所有点都接近,那么这个估算器中的√2因子将过于保守,并将每个点标记为候选者;基于查找表的距离估计器将是更可取的。)

伪代码:

initialize Dmin with the fifth-smallest D from the first five points in list
for point X in list:
    if D(X,P) <= √2 * Dmin:
        insert the tuple (X,D) in the priority-queue of candidates
        if (Dmin>D): Dmin = D
# after first pass, reject candidates with D > √2 * Dmin (use the final value of Dmin)
# ...
# then a second pass on candidates to find lowest 5 exact distances

答案 2 :(得分:2)

由于你的名单很短,我强烈推荐蛮力。只需将所有5000与用户指定的点进行比较即可。它将是O(n),你将获得报酬。

除此之外,四叉树或Kd树是空间细分的常用方法。但是在你的情况下,你最终会在树中进行线性数量的插入,然后进行恒定数量的对数查找......有点浪费,当你最好做一个线性数字距离比较和完成它。

现在,如果你想找到N个最近的点,你会看到计算距离的排序并取第一个N,但那仍然是O(n log n)ish。

编辑:值得注意的是,如果您要重复使用多个查询的点列表,那么构建空间树会变得有价值。

答案 3 :(得分:1)

对于5000个节点,我会计算每个节点的单独x + y距离,而不是直线距离,而不是纯粹的暴力。

一旦您对该列表进行了排序,例如,第5个节点的x + y是38,你可以排除x或y距离> 0的任何节点。这样,你可以排除很多节点,而不必计算直线距离。然后蛮力计算剩余节点的直线距离。

答案 4 :(得分:1)

这些算法不易解释,因此我只会给你一些正确方向的提示。你应该寻找Voronoi Diagrams。使用Voronoi图,您可以在O(n ^ 2 log n)时间内轻松预先计算图形,并在O(log n)时间内搜索最近的点。

预计算是在晚上通过cron作业完成的,并且搜索是实时的。这符合您的规范。

现在你可以保存每个5000点的k个近距离对,然后从Voronoi图中最近的点开始,搜索剩余的4个点。

但请注意,这些算法并不容易实现。

一个很好的参考是:

  • de Berg:Computational Geometry Algorithms Applications(2008)第7.1和7.2章

答案 5 :(得分:0)

由于你有这么几点,我建议你做一个强力搜索,以O(n^2)操作n = 5000操作,或者大约25次,对所有点互相攻击/ 200万次迭代的合适算法,只存储相关结果。这将在C中有超过100毫秒的执行时间,所以我们在Ruby中最多看一两个。

当用户选择一个点时,您可以使用存储的数据以恒定的时间给出结果。

编辑我重新阅读了您的问题,似乎用户提供了自己的最后一点。在这种情况下,每次用户提供一个点时,只需对您的集进行O(n)线性搜索就会更快。

答案 6 :(得分:0)

如果您需要多次重复此操作,使用不同的用户输入位置,但不想实现四叉树(或找不到库实现),那么您可以使用局部敏感的散列(一种非常直观的方法:

  • 取你的(x,y)对并创建两个列表,一个是(x,i),另一个是(y,i),其中i是点的索引
  • 对两个列表进行排序

然后,当给出一个点(X,Y)时,

  • X和Y的二分类
  • 在两个列表上向外扩展,寻找常见指数
  • 用于常见索引,计算精确距离
  • 当X和Y的差异超过当前5点最远距离的确切距离时停止扩展。

你所做的就是说附近的点必须有类似的x和类似的y值...