查找附近点的算法?

时间:2009-05-08 05:20:01

标签: algorithm gis partitioning distance linear-algebra

给定一组带有x,y坐标的数百万个点,从一个位置快速找到前1000个最近点的算法是什么? “快速”在这里意味着在家用电脑上大约100毫秒。

蛮力意味着进行数百万次乘法,然后对它们进行排序。虽然一个简单的Python应用程序可以在不到一分钟的时间内完成,但对于交互式应用程序来说仍然太长了。

点的边界框将是已知的,因此将空间划分为简单网格是可能的。然而,点的分布有些不均匀,所以我怀疑大多数网格方块都是空的,然后突然其中一些将包含很大一部分点。

编辑:不必确切,实际上可能非常不准确。如果前1000名实际上只是前2000名中的一些随机点,那就没什么大不了的了。

编辑:点数集很少改变。

7 个答案:

答案 0 :(得分:18)

如何使用quadtree

您可以将区域划分为矩形,如果区域的点密度较低,矩形较大,如果区域的点密度较高,则矩形会很小。您递归地将每个矩形细分为四个子矩形,直到矩形足够小或包含足够的点。

然后你可以开始查看该位置附近的矩形点,然后向外移动直到找到你的1000点。

这个代码可能会有些复杂,所以也许你应该先用简单的网格尝试一下它是否足够快。

答案 1 :(得分:13)

四叉树很好,但BSP trees保证在O(log n)时间内运行。我认为四叉树需要一个有限的边界体积,并且有一些退化的情况,四边形失败,例如当大量的点占据相同的相对较小的空间时。

话虽如此,Quadtrees可以说更容易实现,并且在大多数常见情况下都非常有效。这是UPS在他们的路由算法中使用的,因为它的缺点不会在实践中造成重大问题,可能是因为城市往往分布在感兴趣的区域。

答案 2 :(得分:6)

您想要使用Quad树或RTree之类的结构。这些是多维索引结构。

关键是使用良好的“空间填充曲线”,这有助于定义点的接近程度。一个简单的空间填充曲线是一个Zorder,但你会对像希尔伯特曲线这样的东西更感兴趣。

http://en.wikipedia.org/wiki/Space_filling_curve

我不知道这些东西的任何预先包装的实现。我最近在2维中实现了自己的RTree,它只支持批量加载和搜索(通过提供的边界框)。

这里的一个缺点是你的点必须包含在一个有限的区域内。我们知道有空间填充曲线适用于不是有限的空间,但我对它们一无所知。

答案 3 :(得分:4)

除了QuadTree和BSP树建议之外,您还应该查找nearest neighbour searching。算法的选择取决于您添加到基础数据集的频率。如果您经常添加和删除,树解决方案是优越的。如果数据更加静态,最近邻搜索和voronoi图可以更快,更好地扩展。

答案 4 :(得分:1)

如果点集很少改变,您还可以考虑使用voronoi图。我不确定这是否有助于更快地找到第一个点,但它应该可以更容易地找到下一个999点。

答案 5 :(得分:0)

我假设这些点位于数据库或某个可搜索的索引位置?如果是这样,它应该很快。从给定点开始,您可以在x和y轴上获得一个范围,并获得该范围内的所有位置(即指定左上角x(a)和y(b)以及最右下角x(c)和y (d))。

然后查询其中y> = b AND y< = d AND x> = a AND x< = c的点。假设您在x和y坐标上有单独的索引,这将很快。 (假设原点在左上角为0,0。)

然后你可以通过z增加(或者如果结果是巨大的)这个范围,直到结果集中的点数是> = 1000.通过一些试运行你应该能够得出一个标准偏差和其他统计数字将帮助您确定要开始的矩形的大小。您的程序还可以根据获得的结果调整自己的程序。

一旦你有了粗略的数据集,就可以通过非常简单的数学计算出每个点和源点之间的距离。

答案 6 :(得分:0)

我知道它被认为不是最快的,如果你想要真正的快速结果看到我发现谷歌的这篇文章我想我会添加我的SQL解决方案,我前一段时间以存储过程的形式。它查找坐标附近的位置并按距离返回。

我希望它有助于某人:)

CREATE PROCEDURE [dbo].[getstores] @lat float,  @lng float AS
DECLARE @radius float, @DegToRad float
SET @DegToRad = 57.29577951
SET @radius = 25000
SELECT TOP 10
    name
    ,sto_lat
    ,sto_lng
    ,postcode
    ,ROUND((ACOS((SIN(@lat/57.2958) * SIN(sto_lat/@DegToRad)) +(COS(@lat/@DegToRad) * COS(sto_lat/@DegToRad) *COS(sto_lng/@DegToRad - @lng/@DegToRad))))* 6387.7, 2) AS distance
FROM store
WHERE (sto_lat >= @lat - (@radius/111))
And (sto_lat <= @lat + (@radius/111))
AND (sto_lng >= @lng - (@radius/111))
AND (sto_lng <= @lng + (@radius/111))
AND (
     ISNUMERIC(sto_lat) = 1
    AND
    ISNUMERIC(sto_lat) = 1
)
ORDER BY distance

注意:我已经声明这不是这个问题的最佳解决方案,对于像我一样在谷歌上发现这个问题的人来说