在二维网格上寻找最近物体的算法
Posted
技术标签:
【中文标题】在二维网格上寻找最近物体的算法【英文标题】:Algorithm for finding nearest object on 2D grid 【发布时间】:2011-03-20 19:12:11 【问题描述】:假设您有一个 2D 网格,网格上的每个点都有 x 个对象(x >=0)。我在考虑 clean 算法时遇到了麻烦,这样当用户指定坐标时,算法会找到最近的坐标(包括指定的坐标),上面有一个对象。
为简单起见,我们假设如果 2 个坐标距离相同,则将返回第一个坐标(或者如果您的算法不能以这种方式工作,那么最后一个坐标无关紧要)。
编辑:距离为 1 的坐标必须是上、下、左或右 1。对角线远离的坐标是 2。
附带说明一下,什么是出色的免费在线算法参考?
【问题讨论】:
旁白:您描述的距离测量通常称为“曼哈顿距离”,或者在数学上称为 L1 距离。 【参考方案1】:更新
有新信息:
假设对角坐标 距离 2 远
这个算法可以工作。该算法以一种螺旋方式向外搜索,从内部开始测试每个“环”中的每个点。
请注意,它不处理越界情况。所以你应该改变它以满足你的需要。
int xs, ys; // Start coordinates
// Check point (xs, ys)
for (int d = 1; d<maxDistance; d++)
for (int i = 0; i < d + 1; i++)
int x1 = xs - d + i;
int y1 = ys - i;
// Check point (x1, y1)
int x2 = xs + d - i;
int y2 = ys + i;
// Check point (x2, y2)
for (int i = 1; i < d; i++)
int x1 = xs - i;
int y1 = ys + d - i;
// Check point (x1, y1)
int x2 = xs + i;
int y2 = ys - d + i;
// Check point (x2, y2)
旧版本
假设在您的二维网格中,(0, 0) 和 (1, 0) 之间的距离与 (0, 0) 和 (1, 1) 之间的距离相同。该算法将起作用。该算法以一种螺旋方式向外搜索,从内部开始测试每个“环”中的每个点。
请注意,它不处理越界情况。所以你应该改变它以满足你的需要。
int xs, ys; // Start coordinates
if (CheckPoint(xs, ys) == true)
return (xs, ys);
for (int d = 0; d<maxDistance; d++)
for (int x = xs-d; x < xs+d+1; x++)
// Point to check: (x, ys - d) and (x, ys + d)
if (CheckPoint(x, ys - d) == true)
return (x, ys - d);
if (CheckPoint(x, ys + d) == true)
return (x, ys - d);
for (int y = ys-d+1; y < ys+d; y++)
// Point to check = (xs - d, y) and (xs + d, y)
if (CheckPoint(x, ys - d) == true)
return (xs - d, y);
if (CheckPoint(x, ys + d) == true)
return (xs - d, y);
【讨论】:
忘记指定对角坐标是 2 远,而不是 1。也许可以对此进行修改。 @random:是的,可以修改。一会儿我试着把它放在那里 @random:考虑到新信息,我添加了新版本的算法:) 在旧版本中,您可能应该将if (CheckPoint(x, ys - d) == true)
更改为if (CheckPoint(xs - d, y))
(第一个if
,第二个内部for
循环); if (CheckPoint(x, ys + d) == true)
和 if (CheckPoint(xs + d, y))
(第二个 if
,第二个内部 for
循环); return (xs - d, y)
和 return (xs + d, y)
(第二个 return
,第二个内部 for
循环)。总之很有趣。
对于任何对算法外观感到好奇的人:jumpshare.com/v/nItG8fU64KQztedXeKYJ 请注意,它不一定会首先找到最接近的像素...例如索引 11 在视觉上比索引 5 更近。跨度>
【参考方案2】:
如果您有对象列表
如果你有一个列表中所有对象的所有位置,这会容易得多,因为你不需要搜索所有的空方块并且可以执行2D distance calculations 来确定离你最近的那个。循环遍历您的对象列表并计算距离如下:
Define your two points. Point 1 at (x1, y1) and Point 2 at (x2, y2).
xd = x2-x1
yd = y2-y1
Distance = SquareRoot(xd*xd + yd*yd)
然后只需选择距离最短的那个。
如果你只有一个二维数组
但是,如果所描述的问题假设一个二维数组,其中如果不首先搜索所有对象就无法列出对象的位置,那么您将不得不进行螺旋循环。
搜索“Spiral Search Method”会出现一些有趣的链接。 Here is some code that does a spiral loop 围绕一个数组,但这并不适用于任意点并向外螺旋,但应该会给你一些关于如何实现你想要的好主意。
这是一个similar question,关于在二维数组中以螺旋顺序填充值。
无论如何,这是我解决问题的方法:
给定点P
,创建一个向量对,指定P
周围的区域。
所以如果P = 4,4
那么你的向量对将是3,3|5,5
在这些范围内循环每个值。
for x = 3 to 5
for y = 3 to 5
check(x,y)
next
next
如果找到值,则退出。如果不是,请再次将界限增加一倍。所以在这种情况下,我们将转到 2,2|6,6
在循环检查值时,确保我们没有进入任何负索引,并确保我们没有超出数组的大小。
另外,如果你将bounds扩展n次,你只需要循环外边界值,你不需要重新检查内值。
哪种方法更快?
这一切都取决于:
阵列的密度 分布技术 对象数量阵列密度
如果你有一个 500x500 的数组,里面有 2 个对象,那么循环列表总是比做螺旋效果好
分发技术
如果存在分布模式(即对象倾向于彼此聚集),那么螺旋可能会执行得更快。
对象数量
如果有一百万个对象,螺旋可能会执行得更快,因为列表技术要求您检查和计算每个距离。
与列表解决方案每次都必须检查每个对象的事实相比,您应该能够通过计算空间被填充的概率来计算最快的解决方案。
但是,使用列表技术,您可以进行一些智能排序以提高性能。这可能值得研究。
【讨论】:
我确实有所有对象的位置,但这有什么帮助? 因为您可以循环遍历整个对象列表,计算该对象与您的起点之间的距离,并找到最短的那个。仔细考虑一下,如果速度很重要,这可能并不总是最快的解决方案,这取决于您的搜索空间有多大。虽然它比外螺旋算法简单得多。 是的,我一开始就是这么做的。现在我已经通过网格进行了表示,我认为使用它的方式可能会更快。 (速度很重要) 我为你添加了一段关于速度的短文 @random :如果查询性能很重要,那么请考虑我的解决方案。是否可以按照我的建议修改网格数据结构?【参考方案3】:如果您的对象很密集,那么仅搜索附近的点可能是找到最近的对象的最佳方式,从中心盘旋而出。如果您的对象是稀疏的,那么quadtree 或相关的数据结构(R-tree 等)可能会更好。这是带有示例的writeup。
我不知道有什么好的在线算法参考,但我可以说,如果你打算写更多的代码而不是偶尔写一行代码,节省你的便士购买CLRS 将是值得的。网上有基于本书的讲座,经过Peteris Krumins的精心注解,但只涵盖了本书的部分内容。这是您需要拥有的少数几本书之一。
【讨论】:
【参考方案4】:以下简单的解决方案假设您可以负担每个网格单元存储额外信息的费用,并且允许将新对象添加到网格的时间成本相对较高。
这个想法是每个单元格都持有对最近占用单元格的引用,因此允许 O(1) 查询时间。 每当将对象添加到位置 (i,j) 时,对周围的单元格进行扫描,覆盖大小增加的环。对于正在扫描的每个单元格,评估其当前最近占用的单元格引用,并在必要时替换它。当被扫描的最后一个环完全没有被修改时,该过程结束。在最坏的情况下,该过程会扫描所有网格单元,但最终当网格变得足够密集时会变得更好。
此解决方案易于实施,可能有很大的空间开销(取决于您的网格在内存中的组织方式),但提供了最佳查询时间。
【讨论】:
这是一个非常酷的解决方案,在某种程度上,当您添加对象而不是尝试获取最近的对象时进行搜索。但是,添加对象比尝试获取最近的对象更常见,添加对象的性能同样重要,甚至更重要(取决于与问题无关的内容)。【参考方案5】:从 4 个方向的起始坐标开始的简单 BFS 足以找到网格上与对象最近的点。
【讨论】:
以上是关于在二维网格上寻找最近物体的算法的主要内容,如果未能解决你的问题,请参考以下文章