如何迭代栅格网格中的环?
Posted
技术标签:
【中文标题】如何迭代栅格网格中的环?【英文标题】:How to iterate over rings in a raster grid? 【发布时间】:2013-05-15 17:25:49 【问题描述】:我们在二维空间中的任何给定位置。该空间被划分为二次单元。我想遍历给定距离内的所有单元格,按照它们与我们的距离顺序。例如,这可能发生在尺寸增加的环中。距离几乎相等的单元格的顺序无关紧要。
如何按到给定位置的距离顺序遍历这些单元格?
【问题讨论】:
【参考方案1】:如果简化为整数网格坐标是可以的(正如您对“几乎相等距离”的评论所暗示的那样),您可以使用下面的代码随着距离的增加逐个遍历单元格。如果您的起点与 (0,0) 不同,则只需将其添加到每个生成的点。关键思想是:
-
从
(d,0)
开始,我们以逆时针方向围绕(0,0)
反复寻找具有“相似”距离(整数部分 = d
)的相邻点,直到我们到达起点。
每个点(除了我们来自的邻居)最多有一个具有“相似”距离的直接邻居(通过边缘)。如果没有直接邻居,则恰好有一个具有“相似”距离的对角邻居(同样,前一个邻居除外)。
到下一个邻居的向量与到原点的向量“几乎”正交。
代码如下:
#include <cmath>
#include <vector>
template <typename T> int sgn(T val)
return (T(0) < val) - (val < T(0));
int dist(double dx, double dy)
return (int)sqrt(dx*dx + dy*dy);
typedef std::pair<int,int> TPoint;
typedef std::vector<TPoint> TPoints;
void generateNeighbourRing(int d, TPoints& ring)
int dx = d;
int dy = 0;
do
ring.push_back(TPoint(dx,dy));
int nx = -sgn(dy);
int ny = sgn(dx);
if (nx != 0 && dist(dx+nx, dy) == d)
dx += nx;
else if (ny != 0 && dist(dx, dy+ny) == d)
dy += ny;
else
dx += nx;
dy += ny;
while (dx != d || dy != 0);
int main()
TPoints points;
const int d_max = 4;
for (int d = 0; d <= d_max; ++d)
generateNeighbourRing(d, points);
printf("spiral for dmax=%d (%d points):\n", d_max, points.size());
for (unsigned int i=0; i<points.size(); ++i)
printf(" (%d,%d),", points[i].first, points[i].second);
printf("\n");
正确性的合理性:
让我们首先看一下与中心单元格距离相等的单元格具有相同颜色的图像(一旦距离被截断,一旦距离被四舍五入):
-- -- -- -- -- --
使用(dx,dy)
,我们迭代等距离的环的单元; (nx,ny)
是一种法向量,在每个半轴上和每个象限内都是常数:
黑色箭头显示每个区域的(nx,ny)
;蓝色箭头表示首先搜索距离相等的(直接)邻居的方向。
接下来我们需要考虑哪些具有相等距离的邻居配置是可能的。由于象限是旋转对称的,因此看第一象限就足够了。两个直接邻居之间到中心像元的距离最多相差 1;对角线朝向或远离中心,距离相差 1 或 2:
。 . .
(这是从直接的不等式得出的。)重要的结论是不可能发生 2x2 等距离的块;最多 4 个邻居可以有相同的距离,形成一个“之字形”:
。 . .
另一个重要结论是,每个单元至少有 2 个距离相等的邻居,同样仅在某些配置中。由此可以推断,如果沿蓝色箭头的邻居具有不同的距离,则沿黑色箭头的邻居具有相同的距离。因此,放入变量ring
的所有点都有距离d
。 (请注意,在第二个else
-branch 中未检查距离。)
接下来我们去终止do ... while
-loop。请注意,随着每次迭代,线 (0,0)-(dx,dy) 与正 x 轴之间的角度会增加。由于距离保持不变,我们最终将离开当前象限并进入下一个象限。而且由于沿着半轴,每个距离恰好出现一次,我们最终将到达起点 (d,0)。
由此还可以得出,没有任何点被重复:在一次调用generateNeighbourRing
中,再次使用同一点开始do ... while
循环的迭代将导致无限循环,因此与终止相矛盾。在generateNeighbourRing
的多次调用中,所有点都不同,因为到中心单元的距离不同。
查看具有相同距离的邻居的可能配置也可以表明将收集所有具有给定距离d
的点。
【讨论】:
有一个 hypot() 或 hypotf() 函数可用于简化距离的计算。同时,您将在 dist() 函数中修复可能的整数溢出。 你的方法是否保证不会两次找到同一个单元格? @Ulrich 感谢您指出 std::hypot。关于溢出:我以前的版本会溢出大小为 2^16 的整数参数(因为转换为浮点数会在整数乘法之后发生),但是当从一开始使用双精度时,整数参数不会溢出,也不会损失精度(双精度有一个 48 位尾数,可以轻松保存一个 32 位整数) @danijar 需要几个条件:A) 没有单元格被占用两次,B) 没有单元格被省略,C) do-while 循环终止。条件 B 至少与 A 一样棘手。两者都来自关于具有相同舍入距离的单元格的相邻结构的关键观察,即“关键思想 2”。多于。这强加了首先选择哪个邻居的顺序(直接“边缘”邻居优先于对角邻居)。上述算法保证了所有三个条件。我认为我应该更详细地阐述这一点。我希望我能找到时间。 @coproc 那太好了,因为还没有完全理解你的算法。但是,您的评论有所帮助,无论如何我都能实现。以上是关于如何迭代栅格网格中的环?的主要内容,如果未能解决你的问题,请参考以下文章