1

想象一个简单的二维网格;网格上的对象可以占据一个以上的单元格(但点始终是连接的)。考虑以下示例,其中我使用字母AB仅用于区分对象(这很有用,因为对象可能彼此靠近放置):

0 1 2 3 4 5 6 7 8 9 10
1 . . . . . . . . . .
2 . . . . . . . . . .
3 . . . A . . . . . .
4 . . A A A . . B B .
5 . . . A . . . B B .
6 . . . . . . . . . .

我需要一种插入新对象的算法,将它们定位在网格上并确保它们不重叠。因此,如果我想嵌入一个新对象(用 表示C)并且它的任何单元格的坐标已经被占用,算法应该找到最近的空闲区域(即点列表)来分配新对象。让我们尝试将对象 C 插入到(4, 3)已被来自的单元格占据的坐标处A

0 1 2 3 4 5 6 7 8 9 10
1 . . . . . . . . . .
2 . C C . . . . . . .
3 . C C A . . . . . .
4 . . A A A . . B B .
5 . . . A . . . B B .
6 . . . . . . . . . .

如您所见,对象被移动以适合对象附近A。我假设搜索应该从占用的单元格开始,顺序为(按方向给出):N、E、S、W,然后在中间方向:NE、SE 等。

你建议如何实现这个算法?

更新:对象位置是左上角。并且最近点是从初始请求位置与周围自由点之间评估的距离获得的。

4

2 回答 2

4

您想按距离增加的顺序迭代可能的位移(即移位)。由于所有位移都是整数,所以位移的平方需要是两个平方的和。以下 python 代码跟踪每个x位移的下一个可能的y位移。它生成对列表。每对表示位移坐标。单个列表中的所有元素与原点的距离相同,而后面列表中的元素将具有更大的距离。因此,至少在距离方面,遍历内部列表的顺序无关紧要。你甚至可能想要随机化这些。

def iter_distance(maxr = 10):
    r = 0
    d = [0]
    while r <= maxr:
        m = maxr*maxr + 1
        for x, y in enumerate(d):
            sq = x*x + y*y
            if sq < m:
                m = sq
                b = []
            if sq == m:
                b.append((x, y))
        for x, y in b:
            d[x] = y + 1
        if b[-1][0] == r:
            r += 1
            d.append(0)
        yield (b +
               [(x, -y) for x, y in b if y] +
               [(-x, y) for x, y in b if x] +
               [(-x, -y) for x, y in b if x*y])

for lst in iter_distance():
    marker = '*'
    for x, y in lst:
        print("{:5} {:5} {:10} {}".format(x, y, x*x + y*y, marker))
        marker = ' '

输出的第一行如下所示:

    0     0          0 *
    0     1          1 *
    1     0          1  
    0    -1          1  
   -1     0          1  
    1     1          2 *
    1    -1          2  
   -1     1          2  
   -1    -1          2  
    0     2          4 *
    2     0          4  
    0    -2          4  
   -2     0          4  
    1     2          5 *
    2     1          5  
    1    -2          5  
    2    -1          5  
   -1     2          5  
   -2     1          5  
   -1    -2          5  
   -2    -1          5  
    2     2          8 *
    2    -2          8  
   -2     2          8  
   -2    -2          8  
    0     3          9 *
    3     0          9  
    0    -3          9  
   -3     0          9  

对于高达 400 的距离(即传递 400 作为maxr参数),您将获得 37,556 个不同距离的 502,625 行,因此您希望动态生成这些,而不是将它们硬编码到应用程序中。但是,您可以使用这些数字来检查您的实施,以防我们中的一个人出错。

如果你关心性能,你可以使用优先级队列而不是数组,这样写:

#include <queue>
#include <utility>
#include <cmath>
#include <iostream>
#include <iomanip>

class displacement {
private:
  int _d;
  int _x;
  int _y;
public:
  displacement() : _d(0), _x(0), _y(0) {}
  displacement(int x, int y) : _d(x*x + y*y), _x(x), _y(y) {}
  int x() const { return _x; }
  int y() const { return _y; }
  int sqd() const { return _d; }
  bool operator<(const displacement& d) const { return sqd() > d.sqd(); }
};

static void print2(int x, int y, int sqd) {
  std::cout << std::setw(10) << x << ' '
            << std::setw(10) << y << ' '
            << std::setw(20) << sqd << ' '
            << std::endl;
}

static void print1(int x, int y, int sqd) {
  print2(x, y, sqd);
  if (y)
    print2(x, -y, sqd);
  if (x) {
    print2(-x, y, sqd);
    if (y)
      print2(-x, -y, sqd);
  }
}

int main(int argc, char** argv) {
  int maxr = 400;
  int maxrsq = maxr*maxr;
  std::priority_queue<displacement> q;
  q.push(displacement(0, 0));
  while (q.top().sqd() <= maxrsq) {
    const displacement& d = q.top();
    int x = d.x();
    int y = d.y();
    int sqd = d.sqd();
    print1(x, y, sqd);
    q.pop();
    q.push(displacement(x, y + 1));
    if (x == y) {
      q.push(displacement(x + 1, y + 1));
    }
    else {
      print1(y, x, sqd);
    }
  }
}

在这种情况下,队列包含单个位移,结果将以任意(可能是实现定义的)顺序打印相同距离的单个位移,而不会将它们收集到列表中。只有给定位移的镜像才会立即打印。这里的代码采用了完全的 8 重对称,因此任何一次存储在队列中的元素数量甚至比目前生成的最大距离还要少,除了最开始的时候。

于 2012-10-16T15:10:52.970 回答
1

我想你有一个检查对象重叠的方法,所以我将定义一个封装孔平面及其已放置对象的对象:如果你要放置的对象与任何对象重叠,则使用返回 truePlane的实例方法当您将其放入平面时或如果它不适合平面,例如当对象大于单个点时将其放置在平面的右下边缘时。boolean overlaps(Object2D object, Point position)Object2Dposition

正如@MvG 指出的那样,只有整数点,所以可能的距离数量有限,所以我会为点之间的距离做一个查找表,以这种方式加快处理速度(我假设是 Java):

    // creates a lookup table with the possible offsets for each possible distance
    static Hashtable<Integer, List<Point>> createLookupPointOffsets(int gridWidth, int gridHeight)
    {
        Hashtable<Integer, List<Point>> l = new Hashtable<Integer, List<Point>>();
        for (int i = 0; i <= gridWidth; i++)
        {
            int i2 = i * i;
            for (int j = 0; j <= gridHeight; j++)
            {
                int sqrDistance = i2 + j * j; // distance^2
                List<Point> ps = l.get(sqrDistance);
                if (ps == null)
                {
                    ps = new List<Point>();
                    l.put(sqrDistance, ps);
                }
                ps.add(new Point(i, j));
            }
        }
    }

这样,您将获得可能距离的 1/4 偏移量,对于其他距离,您只需通过 x 或 y 或两个轴反映点。为了评估使用此索引偏移量放置对象的最接近点,请将此方法添加到 中Plane,我假设变量mIndexedOffsets已使用最后一个方法的结果进行初始化:

    // returns true if placed the object
    boolean place(Object2D object, Point position)
    {
        if (overlaps(object, position))
        {
            Integer[] distances = new Integer[mIndexedOffsets.keySet().size()];
            distances = mIndexedOffsets.keySet().ToArray(distances);
            // sort the keys in crescent order
            for (int k = 0; k < distances.length - 1; k++)
            {
                for (int l = k + 1; l < distances.length; l++)
                {
                    if (distances[k] > distances[l])
                    {
                        int t = distances[k];
                        distances[k] = distances[l];
                        distances[l] = t;
                    }
                }
            }
            for (int i = 0; i < distances.length; i++)
            {
                List<Point> ps = mIndexedOffsets.get(distances[i]);
                for (int j = 0; j < ps.size(); j++)
                {
                    Point p = ps.get(j);
                    Point newPoint = (Point) position.clone();
                    newPoint.x += p.x;
                    newPoint.y += p.y;
                    if (!overlaps(object, newPoint)
                    {
                        put(object, newPoint);
                        return true;
                    }
                    // test with the reflected points
                    newPoint = (Point) position.clone();
                    newPoint.x -= p.x;
                    newPoint.y += p.y;
                    if (!overlaps(object, newPoint)
                    {
                        put(object, newPoint);
                        return true;
                    }
                    newPoint = (Point) position.clone();
                    newPoint.x += p.x;
                    newPoint.y -= p.y;
                    if (!overlaps(object, newPoint)
                    {
                        put(object, newPoint);
                        return true;
                    }
                    newPoint = (Point) position.clone();
                    newPoint.x -= p.x;
                    newPoint.y -= p.y;
                    if (!overlaps(object, newPoint)
                    {
                        put(object, newPoint);
                        return true;
                    }
                }
            }
        }
        else
        {
            put(object, position); // puts the object in the desired position
            return true;
        }
        return false;
    }

希望能帮助到你。

于 2012-10-16T17:13:27.503 回答