Algorithm 基于网格划分的二维最近邻搜索

Algorithm 基于网格划分的二维最近邻搜索,algorithm,geometry,nearest-neighbor,Algorithm,Geometry,Nearest Neighbor,我在一个集合中有一个相当大的2D点集合(~20000),对于x-y平面中的每个点,我想确定该集合中最接近的点。(实际上,点的类型不同,我只想知道哪种类型最接近。x-y平面是位图,比如640x480。) 从到问题“”我有了制作网格的想法。我创建了N*M C++向量并将点放在向量中,这取决于它落入哪个容器。其思想是,您只需检查箱子中点的距离,而不是所有点的距离。如果存储箱中没有点,则以螺旋方式继续使用相邻的存储箱 不幸的是,我后来才读到奥利·查尔斯沃思的评论: 不幸的是,不只是相邻(考虑单元格中的两

我在一个集合中有一个相当大的2D点集合(~20000),对于x-y平面中的每个点,我想确定该集合中最接近的点。(实际上,点的类型不同,我只想知道哪种类型最接近。x-y平面是位图,比如640x480。)

从到问题“”我有了制作网格的想法。我创建了N*M C++向量并将点放在向量中,这取决于它落入哪个容器。其思想是,您只需检查箱子中点的距离,而不是所有点的距离。如果存储箱中没有点,则以螺旋方式继续使用相邻的存储箱

不幸的是,我后来才读到奥利·查尔斯沃思的评论:

不幸的是,不只是相邻(考虑单元格中的两点) 向东可能比单元格中直接向东北的点更近, 例如,这个问题在更高的维度上变得更糟)。 另外,如果相邻的单元恰好少于10个呢 他们的分数是多少?在实践中,您需要“螺旋输出”

幸运的是,我已经找到了螺旋式代码(一个不错的解决方案,在同一个问题中还有其他版本)。但我仍然有一个问题:

  • 如果我在一个单元格中发现命中,相邻单元格中可能会有更近的命中(黄色是我的探针,红色是错误的选择,绿色是实际的最近点):

  • 如果我在相邻的一个牢房里发现了一个命中目标,那么在2步之外的一个牢房里可能会有一个命中目标,正如奥利·查尔斯沃思所说:

  • 但更糟糕的是,如果我在两步之外的牢房里发现了一个命中目标,那么在三步之外的牢房里还有一个更近的命中目标!这意味着我必须考虑所有的细胞与DX,Dy=- 3…3或49个细胞!

现在,在实践中,这种情况不会经常发生,因为我可以选择我的垃圾箱大小,这样单元格就足够满了。尽管如此,我还是希望得到一个正确的结果,而不是遍历所有点


那么,我如何确定何时停止“螺旋”或搜索呢?我听说有一种方法使用多个重叠网格,但我不太明白。有可能挽救这种网格技术吗?

由于位图的尺寸不大,并且您希望计算每个
(x,y)
的最近点,因此可以使用动态编程

V[i][j]
为从
(i,j)
到集合中最近点的距离,但只考虑集合中位于“矩形”[(1,1),(i,j)]中的点

然后
V[i][j]=0
如果
(i,j)
中有一个点,或者
V[i][j]=min(V[i'][j']+dist((i,j),(i',j'))
其中
(i',j')
(i,j)
的三个邻居之一:

i、 e

  • (i-1,j)
  • (i,j-1)
  • (i-1,j-1)
这将为您提供最小距离,但仅适用于“左上”矩形。我们对“右上”、“左下”和“右下”方向做同样的操作,然后取最小值


复杂度是O(平面大小),这是最优的。

一个解决方案是用不同的网格大小构造多个分区

假设您在级别1,2,4,8创建分区

现在,搜索网格大小为1的点(基本上是在9个正方形中搜索)。如果搜索区域中有一个点,并且到该点的距离小于1,则停止。否则,请转到下一个网格大小


与只创建一级分区相比,您需要构建的网格数量大约是创建一级分区的两倍。

对于您的任务,通常使用
点四叉树,尤其是当点分布不均匀时

要保存主内存,可以使用使用存储桶的PM或PMR四叉树

你在你的单元中搜索,在最坏的情况下,搜索围绕该单元的所有四元单元

您还可以使用
k-d树

我正在尝试的解决方案

  • 首先,制作一个网格,使每个框平均有1个点(如果需要更大的扫描,则更多)
  • 选择中心框。继续以循环方式选择邻居框,直到找到至少一个邻居。此时,可以选择1个或9个左右的框
  • 再选择一层相邻框
  • 现在,您有了一个相当小的点列表,通常不超过10个点,您可以在距离公式中输入这些点以找到最近的邻居
由于每个框平均有1个点,因此您通常会选择9个框并比较9个距离。可以根据数据集属性调整网格大小,以获得更好的结果


此外,如果您的数据存在很大差异,您可以尝试两级网格(甚至更多),因此,如果选择有效并在单个查询中返回50个以上的点,请使用1/10大小的网格开始下一次网格搜索…

您的点集是静态的还是动态的?对于一个点集执行了多少个“查找最近的”查询?是否有理由不使用现有的开发良好的库,例如内部使用kd树的ANN?还是出于好奇?k-d树专门处理这种情况,它考虑了拆分平面的其他方面: