想象一个简单的2D网格;网格上的对象可以占用多个单元格(但这些点始终是连接的)。请考虑以下示例,其中我使用字母A
和B
来区分对象(它很有用,因为对象可能彼此靠近):
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等。
您如何建议实施此算法?
更新:对象位置是左上角。 最近的点是从初始请求位置和周围自由点之间的距离获得的。
答案 0 :(得分:3)
您希望按照距离增加的顺序迭代可能的位移(即移位)。由于所有位移都是整数,因此平方位移需要为sums of two squares。以下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
参数),你会得到502,625行37,556个不同的距离,所以你想要动态生成这些,而不是将它们硬编码到应用。但是,如果我们中的一个人犯了错误,您可以使用这些数字来检查您的实施情况。
如果您担心性能,可以使用优先级队列而不是数组,并按如下方式编写:
#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倍对称,因此在任何时候存储在队列中的元素数量甚至小于到目前为止生成的最大距离,除非在最开始。
答案 1 :(得分:1)
我想你有一个方法来检查对象重叠,所以,我将定义一个封装孔平面及其已经放置的对象的对象:Plane
,实例方法boolean overlaps(Object2D object, Point position)
,如果返回true,则返回true Object2D
- 要放置的对象,当您将任何对象放入position
或者它不适合平面时,将任何对象重叠到平面中,就像放在右下方时一样当物体大于单个点时平面的边缘。
正如@MvG指出的那样,只有整数点,所以有一个限制的可能距离数,所以我会对点之间的距离做一个查找表,以这种方式加速进程(我假设Java):< / p>
// 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;
}
希望它有所帮助。