到达目的地有多少步?高效的洪水填充

时间:2011-11-14 10:24:55

标签: algorithm fill

我想计算距目标细胞的细胞距离,使用四向运动的数量来达到某种程度。因此,紧邻目的地的四个单元具有1的距离,并且每个单元的四个基本方向上的距离为2,依此类推。最大距离可能在16或20左右,有些细胞被障碍占据;距离可以围绕它们流动,但不能通过它们流动。

我想将输出存储到2D数组中,我希望能够非常快速地为更大的迷宫地图上的任何目的地计算这个“距离地图”。

我成功地使用洪水填充的变体进行了操作,其中I将相邻未填充单元格的增量距离放在优先级队列中(使用C ++ STL)。

我对这些功能感到满意,现在希望专注于优化代码,因为它对性能非常敏感。

可能有什么狡猾和快速的方法?

An example map for all cells within four moves of the target x; black denotes barrier

4 个答案:

答案 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)

20世纪70年代的8位计算机采用了具有相同算法复杂度的优化,但在典型情况下,实际硬件上的速度要快得多。

从最初的方块开始,向左和向右扫描,直到找到“墙”。现在你的“跨度”是一平方高,N平方宽。将跨度标记为“已填充”,在这种情况下,每个正方形与初始正方形的距离。

对于当前跨度上下的每个方格,如果它不是“墙”或已经填充,请选择它作为跨度的新原点。

重复直到找不到新的跨度。

由于水平行倾向于连续存储在内存中,因此该算法倾向于将缓存抖动远远小于没有水平搜索偏差的缓存。

此外,由于在最常见的情况下,从堆栈推送和弹出的项目数量减少(跨越而不是单个块),维护堆栈的时间就会减少。