1

我们处于二维空间中的任何给定位置。该空间被划分为二次单元。我想遍历给定距离内的所有单元格,按照它们与我们的距离顺序。例如,这可能发生在尺寸增加的环中。距离几乎相等的单元格的顺序无关紧要。

如何按到给定位置的距离顺序遍历这些单元格?

4

1 回答 1

2

如果简化为整数网格坐标是可以的(正如您对“几乎相等距离”的评论所暗示的那样),您可以使用下面的代码随着距离的增加逐个遍历单元格。如果您的起点与 (0,0) 不同,则只需将其添加到每个生成的点。关键思想是:

  1. 从我们开始,我们以逆时针方向(d,0)迭代地寻找具有“相似”距离(整数部分 = )的相邻点,直到我们到达起点。d(0,0)
  2. 每个点(除了我们来自的邻居)最多有一个具有“相似”距离的直接邻居(通过边缘)。如果没有直接邻居,则恰好有一个具有“相似”距离的对角邻居(同样除了前一个邻居)。
  3. 到下一个邻居的向量“几乎”正交于到原点的向量。

这是代码:

#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 个邻居可以有相同的距离,形成一个“之字形”:

没有等距离的 2x2 方块 . . .最多 4 个邻居

另一个重要的结论是每个单元至少有两个距离相等的邻居,同样仅在某些配置中。由此可以推断,如果沿蓝色箭头的邻居具有不同的距离,则沿黑色箭头的邻居具有相同的距离。因此,放入变量中的所有点ring都有距离d。(请注意,在第二个else分支中,未检查距离。)

接下来我们去终止do ... while-loop。请注意,随着每次迭代,线 (0,0)-(dx,dy) 与正 x 轴之间的角度会增加。由于距离保持不变,我们最终将离开当前象限并进入下一个象限。而且由于沿着半轴,每个距离恰好出现一次,我们最终将到达起点 (d,0)。

由此还得出,没有点被取两次:在一次调用中,再次使用相同的点generateNeighbourRing开始循环迭代do ... while将导致无限循环,因此与终止相矛盾。generateNeighbourRing由于到中心单元的距离不同,所有点的多次调用都不同。

查看具有相同距离的邻居的可能配置也可以表明d将收集具有给定距离的所有点。

于 2013-05-15T19:19:17.297 回答