A* 寻路缓慢

Posted

技术标签:

【中文标题】A* 寻路缓慢【英文标题】:A* pathfinding slow 【发布时间】:2012-06-07 15:17:11 【问题描述】:

我目前正在研究 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];
    

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

谢谢。

【问题讨论】:

我会使用 ctime (cplusplus.com/reference/clibrary/ctime) 之类的东西,并开始计时您正在执行的不同操作,以查找大部分计算时间花费在哪里。有时很难判断算法在哪里花费时间,我发现这是在所有其他方法都失败时找到它的好方法。 顺便说一句,我不想​​吹毛求疵,但距离的正确名称是曼哈顿(就像城市一样,因为它的灵感来自城市街区)。 【参考方案1】:

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

当然,由于您的启发式方法看起来很完美,您应该从排除启发式预测为精确的路线中获得很大的好处,只要到目前为止已知的最佳路线。 (如果您的启发式是正确的,即永远不会高估剩余距离,这是安全的。

【讨论】:

A* 运行时间为 O(n log n) - 对于 20x20 的迷宫 (n=400),没有理由需要接近 6 秒。 @BlueRaja-DannyPflughoeft 在没有墙的n x n 迷宫中,一个好的实现应该在O(n log(n)) 时间内运行,日志因子来自堆操作。【参考方案2】:

这里有一个很大的提示。

如果您找到两条通向同一个单元格的路径,您可以随时丢弃较长的一条。如果有平局,您可以扔掉第二条到达那里。

如果您在没有其他优化的情况下实现这一点,搜索速度会变得非常快。

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

【讨论】:

【参考方案3】:

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


尝试将openList 更改为应有的优先级队列,并将visitedList 更改为哈希表。然后整个for 循环可以被dequeue 替换 - 请确保检查visitedList.Contains(node) 是否在入队之前!

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

【讨论】:

当我找到下一个单元格时,我会检查这个。如果单元格不在访问列表中,它是另一个返回 true 的函数。 @JakeRunzer 检查不起作用。如果检查工作正常,那么您最多必须生成 400 条路径。即使其他一切都是低级多项式,即使使用慢速语言(C++ 不是),您也将花费不到 6 秒的时间。 我会按照你说的更改打开和访问列表。另外,我如何防止每次都重新计算曼哈顿距离? @Jake:入队节点时计算。【参考方案4】:

你不是经常回溯吗?

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

如果是这样,这可能说明了您的算法所花费的时间。

【讨论】:

以上是关于A* 寻路缓慢的主要内容,如果未能解决你的问题,请参考以下文章

游戏AI之A*寻路

寻路算法和逻辑算法之间异同点都有哪些

寻路开启

训练寻路

unity3d 如何动态寻路?

unity 自动寻路怎么设置障碍