如何使A*算法给出最短路径?(见随附图片)

How can I make the A* algorithm give me the shortest path? (see picture enclosed)

本文关键字:最短路径 何使 算法      更新时间:2023-10-16

我使用了astar算法的Justin Heyes-Jones实现。我的启发式函数就是欧几里得距离。在所附的图形中(很抱歉质量不好)描述了一种特定情况:假设我们从节点1转到节点2。最短的方法是通过节点a-b-c-d-e。但欧几里得启发式的逐步Astar将为我们提供通过以下节点的方法:a-b’-c’-d’-e,我理解为什么会发生这种情况。但是我该怎么做才能让它返回最短的路径?!

astar 的伪最短路径查找

真正的路线图导入代码:

#include "search.h"
class ArcList;
class MapNode
{
public:
    int x, y;       // ���������� ����
    MapNode();
    MapNode(int X, int Y);
    float Get_h( const MapNode & Goal_node );
    bool GetNeighbours( AStarSearch<MapNode> *astarsearch, MapNode *parent_node );
    bool IsSamePosition( const MapNode &rhs );
    void PrintNodeInfo() const;
    bool operator == (const MapNode & other) const;
    void setArcList( ArcList * list );
private:
    ArcList * list;
};
class Arc
{
public:
    MapNode A1;
    MapNode B1;
    Arc(const MapNode & a, const MapNode & b);
};
class ArcList
{
public:
    void setArcs( const std::vector<Arc> & arcs );
    void addArc( const Arc & arc );
    size_t size() const;
    bool addNeighbours( AStarSearch<MapNode> * astarsearch, const MapNode & neighbour );
private :
    std::vector<Arc> arcs;
};
std::vector <MapNode> FindPath(const MapNode & StartNode, const MapNode & GoalNode)
{
    AStarSearch<MapNode> astarsearch;
    astarsearch.SetStartAndGoalStates( StartNode, GoalNode );
    unsigned int SearchState;
    unsigned int SearchSteps = 0;
    do
    {
        if ( SearchSteps % 100 == 0)
            std::cout << "making step " << SearchSteps << endl;
        SearchState = astarsearch.SearchStep();
        SearchSteps++;
    }
    while ( SearchState == AStarSearch<MapNode>::SEARCH_STATE_SEARCHING );
    std::vector<MapNode> S;
    if ( SearchState == AStarSearch<MapNode>::SEARCH_STATE_SUCCEEDED )
    {
        int steps = 0;
        for ( MapNode * node = astarsearch.GetSolutionStart(); node != 0; node = astarsearch.GetSolutionNext() )
        {
            S.push_back(*node);
//            node->PrintNodeInfo();
        }
        astarsearch.FreeSolutionNodes();
    }
    else if ( SearchState == AStarSearch<MapNode>::SEARCH_STATE_FAILED )
    {
        throw " SEARCH_FAILED ";
    }
    return S;
}

函数FindPath为我提供了结果节点的矢量。

这里是addNeighbours方法:

bool ArcList::addNeighbours( AStarSearch<MapNode> * astarsearch, const MapNode & target )
{
    assert(astarsearch != 0);
    bool found = false;
    for (size_t i = 0; i < arcs.size(); i++ )
    {
        Arc arc = arcs.at(i);
        if (arc.A1 == target)
        {
            found = true;
            astarsearch->AddSuccessor( arc.B1 );
        }
        else if (arc.B1 == target )
        {
            found = true;
            astarsearch->AddSuccessor( arc.A1 );
        }
    }
    return found;
}

和get_h方法:

float MapNode::Get_h( const MapNode & Goal_node )
{
    float dx = x - Goal_node.x;
    float dy = y - Goal_node.y;
    return ( dx * dx  + dy * dy );
}

我知道这不是精确的距离(这里不取平方根)——这样做是为了在评估时节省一些机器资源。

当您使用A*图搜索时,即您只考虑对节点的第一次访问,而忽略未来的访问,事实上,当您的启发式不一致时,可能会发生这种情况。如果启发式不一致,并且您使用图形搜索(您保留一个已访问状态的列表,如果您已经遇到一个状态,则不会再次展开它),则您的搜索不会给出正确的答案。

然而,当您使用具有可接受启发式的A*树搜索时,您应该得到正确的答案。树搜索和图搜索的区别在于,在树搜索中,每次遇到状态时都会展开它。因此,即使一开始算法决定采用更长的b',c',d'路径,但后来它会返回到a,再次展开它,并发现b,c,d路径实际上更短。

因此,我的建议是,要么使用树搜索而不是图搜索,要么选择一致的启发式方法。

关于一致性的定义,请参见例如:Wikipedia:A*搜索算法

编辑:虽然以上仍然是真的,但这种启发确实是一致的,我为造成的混乱道歉。

第二版:虽然启发式本身是可接受的和一致的,但实现不是。为了性能,你决定不做平方根运算,这使得你的启发式算法不可接受,这也是你得到错误结果的原因。

对于未来,最好先尽可能天真地实现您的算法。这通常有助于保持它们的可读性,并且它们不太容易出现错误。如果有漏洞,就更容易发现。因此,我的最后建议是,除非你需要,或者其他一切都很好,否则不要进行优化。否则你可能会遇到麻烦。

似乎在get_h方法中取平方根已经解决了这个问题。事实证明,我的启发式是不可接受的(至少我认为这可以解释它)。特别感谢Laky和justinhj的帮助!!!