A* 寻路速度慢

A* pathfinding slow

本文关键字:速度慢 寻路      更新时间:2023-10-16

我目前正在研究A*搜索算法。该算法只是解决文本文件迷宫。我知道 A* 算法应该非常快速地找到完成。我的似乎需要 6 秒才能在没有墙壁的 20x20 迷宫中找到路径。它确实找到了正确的路径,只需要永远这样做。

如果我知道代码的哪一部分是问题所在,我会发布它,但我真的不知道出了什么问题。所以这是我使用的算法...

 while(!openList.empty()) {  
    visitedList.push_back(openList[index]);
    openList.erase(openList.begin() + index);
    if(currentCell->x_coor == goalCell->x_coor && currentCell->y_coor == goalCell->y_coor)          
    }
        FindBestPath(currentCell);
        break;
    }
    if(map[currentCell->x_coor+1][currentCell->y_coor] != wall)
    {
    openList.push_back(new SearchCell(currentCell->x_coor+1,currentCell->y_coor,currentCell));
    }
    if(map[currentCell->x_coor-1][currentCell->y_coor] != wall) 
    {
        openList.push_back(new SearchCell(currentCell->x_coor-1,currentCell->y_coor,currentCell));
    }
    if(map[currentCell->x_coor][currentCell->y_coor+1] != wall) 
    {
        openList.push_back(new SearchCell(currentCell->x_coor,currentCell->y_coor+1,currentCell));
    }
    if(map[currentCell->x_coor][currentCell->y_coor-1] != wall) 
    {
        openList.push_back(new SearchCell(currentCell->x_coor,currentCell->y_coor-1,currentCell));
    }
    for(int i=0;i<openList.size();i++) {
        openList[i]->G = openList[i]->parent->G + 1;
        openList[i]->H = openList[i]->ManHattenDistance(goalCell);
    }
    float bestF = 999999;
    index = -1;
    for(int i=0;i<openList.size();i++) {
        if(openList[i]->GetF() < bestF) {
            for(int n=0;n<visitedList.size();n++) {
                if(CheckVisited(openList[i])) {
                    bestF = openList[i]->GetF();
                    index = i;
                }
            }
        }
    }
    if(index >= 0) {
        currentCell = openList[index];
    }
}

我知道这段代码很混乱,不是最有效的做事方式,但我认为它仍然应该比它更快。任何帮助将不胜感激。

谢谢。

> 您的 20x20 迷宫没有墙壁,因此许多路线的长度都相同。事实上,我估计有数万亿条等效路线。当你考虑到这一点时,它似乎并没有那么糟糕。

当然,由于您的启发式方法看起来很完美,因此排除启发式预测的路线与迄今为止已知的最佳路线一样长,您应该会获得很大的好处。(如果您的启发式是正确的,这是安全的,即永远不要高估剩余距离)。

这是一个很大的提示。

如果你找到两条通往同一牢房的路径,你总是可以扔掉更长的一条。 如果有领带,你可以扔掉第二个到达那里。

如果您实现这一点,没有其他优化,搜索将变得非常快。

其次,A* 算法应该只在当前单元格加上启发式的长度超过当前单元格加上任何其他节点的启发式的长度时进行回溯。 如果你实现了这一点,那么它应该直接找到一条路径并停止。 为了方便起见,您需要将路径存储在优先级队列(通常使用堆实现)中,而不是向量中。

openList.erase 是 O(n),而以 for(int i=0;i<openList.size();i++) 开头的 for 循环是 O(n^2),因为调用了 CheckVisited - 这些每次迭代都会调用,使您的整体算法 O(n^3)。 A* 为 O(n log n)。


尝试将openList更改为优先级队列,并将其visitedList更改为哈希表。 然后,整个for循环可以用dequeue替换 - 确保在排队检查是否visitedList.Contains(node)

此外,无需在每次迭代时重新计算每个节点的ManHattenDistance,因为它永远不会更改。

你不是经常回溯吗?

当当前最佳解决方案变得比之前访问过的另一个路由更差时,A* 算法会回溯。在你的情况下,由于没有墙壁,所以所有路线都是好的,永远不会死(正如MSalters正确指出的那样,有几条)。当你迈出一步时,你的路线会变得比其他所有短一步的路线更糟糕。

如果这是真的,这可能解释了你的算法所花费的时间。