我想计算距目标细胞的细胞距离,使用四向运动的数量来达到某种程度。因此,紧邻目的地的四个单元具有1的距离,并且每个单元的四个基本方向上的距离为2,依此类推。最大距离可能在16或20左右,有些细胞被障碍占据;距离可以围绕它们流动,但不能通过它们流动。
我想将输出存储到2D数组中,我希望能够非常快速地为更大的迷宫地图上的任何目的地计算这个“距离地图”。
我成功地使用洪水填充的变体进行了操作,其中I将相邻未填充单元格的增量距离放在优先级队列中(使用C ++ STL)。
我对这些功能感到满意,现在希望专注于优化代码,因为它对性能非常敏感。
可能有什么狡猾和快速的方法?
答案 0 :(得分:2)
我认为你已经做好了一切。如果编码正确,则需要O(n)时间和O(n)内存来计算洪水填充,其中n是单元格的数量,并且可以证明它不可能做得更好(一般情况下) )。填充完成后,只需返回任何目的地的距离为O(1),就可以很容易地看出它也可以做得更好。
因此,如果您想要优化性能,您只能专注于CODE LOCAL OPTIMIZATION。这不会影响渐近但可以显着提高您的实际执行时间。但是如果没有真正看到源代码,很难给你任何代码优化的建议。
因此,如果您真的想看到优化代码,请参阅以下内容(Pure C):
int* BFS()
{
int N, M; // Assume we have NxM grid.
int X, Y; // Start position. X, Y are unit based.
int i, j;
int movex[4] = {0, 0, 1, -1}; // Move on x dimension.
int movey[4] = {1, -1, 0, 0}; // Move on y dimension.
// TO DO: Read N, M, X, Y
// To reduce redundant functions calls and memory reallocation
// allocate all needed memory once and use a simple arrays.
int* map = (int*)malloc((N + 2) * (M + 2));
int leadDim = M + 2;
// Our map. We use one dimension array. map[x][y] = map[leadDim * x + y];
// If (x,y) is occupied then map[leadDim*x + y] = -1;
// If (x,y) is not visited map[leadDim*x + y] = -2;
int* queue = (int*)malloc(N*M);
int first = 0, last =1;
// Fill the boarders to simplify the code and reduce conditions
for (i = 0; i < N+2; ++i)
{
map[i * leadDim + 0] = -1;
map[i * leadDim + M + 1] = -1;
}
for (j = 0; j < M+2; ++j)
{
map[j] = -1;
map[(N + 1) * leadDim + j] = -1;
}
// TO DO: Read the map.
queue[first] = X * leadDim + Y;
map[X * leadDim + Y] = 0;
// Very simple optimized process loop.
while (first < last)
{
int current = queue[first];
int step = map[current];
for (i = 0; i < 4; ++i)
{
int temp = current + movex[i] * leadDim + movey[i];
if (map[temp] == -2) // only one condition in internal loop.
{
map[temp] = step + 1;
queue[last++] = temp;
}
}
++first;
}
free(queue);
return map;
}
代码可能看起来很棘手。当然,它看起来并不像OOP(我实际上认为OOP粉丝会讨厌它),但如果你想要一些非常快的东西,那就是你需要的东西。
答案 1 :(得分:1)
这是BFS的常见任务。复杂性是O(cellsCount)
我的c ++实现:
vector<vector<int> > GetDistance(int x, int y, vector<vector<int> > cells)
{
const int INF = 0x7FFFFF;
vector<vector<int> > distance(cells.size());
for(int i = 0; i < distance.size(); i++)
distance[i].assign(cells[i].size(), INF);
queue<pair<int, int> > q;
q.push(make_pair(x, y));
distance[x][y] = 0;
while(!q.empty())
{
pair<int, int> curPoint = q.front();
q.pop();
int curDistance = distance[curPoint.first][curPoint.second];
for(int i = -1; i <= 1; i++)
for(int j = -1; j <= 1; j++)
{
if( (i + j) % 2 == 0 ) continue;
pair<int, int> nextPoint(curPoint.first + i, curPoint.second + j);
if(nextPoint.first >= 0 && nextPoint.first < cells.size()
&& nextPoint.second >= 0 && nextPoint.second < cells[nextPoint.first].size()
&& cells[nextPoint.first][nextPoint.second] != BARRIER
&& distance[nextPoint.first][nextPoint.second] > curDistance + 1)
{
distance[nextPoint.first][nextPoint.second] = curDistance + 1;
q.push(nextPoint);
}
}
}
return distance;
}
答案 2 :(得分:0)
从递归实现开始:(未经测试的代码)
int visit( int xy, int dist) {
int ret =1;
if (array[xy] <= dist) return 0;
array[xy] = dist;
if (dist == maxdist) return ret;
ret += visit ( RIGHT(xy) , dist+1);
...
same for left, up, down
...
return ret;
}
你需要处理初始化和边缘情况。而且你必须决定你是想要一个二维数组还是一个一维数组。
下一步可能是使用待办事项列表并删除递归,第三步可能是添加一些位掩码。
答案 3 :(得分:0)
从最初的方块开始,向左和向右扫描,直到找到“墙”。现在你的“跨度”是一平方高,N平方宽。将跨度标记为“已填充”,在这种情况下,每个正方形与初始正方形的距离。
对于当前跨度上下的每个方格,如果它不是“墙”或已经填充,请选择它作为跨度的新原点。
重复直到找不到新的跨度。
由于水平行倾向于连续存储在内存中,因此该算法倾向于将缓存抖动远远小于没有水平搜索偏差的缓存。
此外,由于在最常见的情况下,从堆栈推送和弹出的项目数量减少(跨越而不是单个块),维护堆栈的时间就会减少。