首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >给出不正确输出的*图算法

给出不正确输出的*图算法
EN

Stack Overflow用户
提问于 2014-02-10 13:13:43
回答 1查看 196关注 0票数 6

我正在为A*编写一个图形版本,以解决8个谜题问题,我实现了一个树版本测试它,它运行良好。我只是通过跟踪访问过的节点来扩展树版本来实现图形版本。

这是原始的树版本:

代码语言:javascript
复制
int AStarTreeVersion (Node initialState){
    priority_queue<Node, vector<Node>, greater<Node> > fringe;
    fringe.push(initialState);

    while (true){

        if (fringe.empty()) // no solution
            return -1;

        Node current = fringe.top();
        fringe.pop();

        if (current.isGoal())
            return current.getDistance();

        auto successors = current.getSuccessors();

        for (auto s : successors)

            if (s != current)
                fringe.push(s);

    }

}

图的版本是:

代码语言:javascript
复制
int AStarGraphVersion(Node initialState){
    priority_queue<Node, vector<Node>, greater<Node> > fringe;
    fringe.push(initialState);

    unordered_set<Node> visited; // <---
    visited.insert(initialState);// <---


    while (true){

        if (fringe.empty()) // no solution
            return -1;

        Node current = fringe.top();
        fringe.pop();

        if (current.isGoal())
            return current.getDistance();

        auto successors = current.getSuccessors();

        for (auto s : successors){
            auto pair = visited.insert(s); //<--
            if (pair.second) //<--
                fringe.push(s); //<--
        }
    }
}

我添加了一个箭头来表示这两个版本之间的差异。有人能看出是怎么回事吗?

下面是测试用例,用于解决8项难题:

代码语言:javascript
复制
array<int, 9> a=  {1, 6, 4, 8, 7, 0, 3, 2, 5};
Node ini(a);
cout<<"Tree solution "<<AStarTreeVersion(ini)<<endl;
cout<<"Graph solution "<<AStarGraphVersion(ini)<<endl;

输出:

代码语言:javascript
复制
Tree solution 21
Graph solution 23

编辑

下面是Node类的相关细节:

代码语言:javascript
复制
class Node {
public:
    bool operator>(const Node& that) const
    {return this->getHeuristicValue() > that.getHeuristicValue() ;}

    friend inline bool operator==(const Node & lhs, const Node & rhs)
                       { return lhs.board == rhs.board;}
    friend inline bool operator!=(const Node & lhs, const Node & rhs)
                      { return ! operator==(lhs,rhs) ;}

        size_t getHashValue ()const{
            size_t seed = 0;
        for (auto  v : board)
            seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
            return seed;       
    }


private:
    array<int, 9> board;
};

下面是我如何重载hash模板:

代码语言:javascript
复制
namespace std {
    template <> struct hash<Node>
    {
        size_t operator()(const Node & t) const
        {
            return t.getHashValue();
        }
    };
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-02-14 22:01:37

我想你的问题是:

代码语言:javascript
复制
for (auto s : successors){
   auto pair = visited.insert(s); //<--
   if (pair.second) //<--
       fringe.push(s); //<--
   }
}

*搜索是通过维护这些节点的节点边缘和候选距离来完成的。当第一次插入这些节点时,并不保证这些节点的候选距离是准确的。例如,想象一个图,其中有一个从开始节点到目标节点与成本∞的直接连接,以及另一条距离为4但通过其他中间节点的路径。当扩展初始节点时,它将把目标节点放入具有候选距离∞的优先级队列中,因为从该节点到目标节点有一个直接边缘。这是错误的距离,但通常情况下,这是好的-稍后,A*将发现另一条路线,并降低候选距离的目标从∞到4加上启发式。

但是,上面的代码没有正确地实现这一点。具体来说,它确保节点只插入优先级队列一次。这意味着,如果初始候选距离不正确,则不会更新队列中的优先级,以在找到更好的路径时反映更新的候选距离。在基于树的版本中,这不是一个问题,因为您将在稍后的优先级队列中插入目标节点的第二个副本,并具有正确的优先级。

有几种方法可以解决这个问题。最简单的方法是:不要将节点标记为已访问的节点,直到您将其从优先级队列中删除并进行处理。这意味着,每当您看到某个节点的路径时,都应该使用估计的距离将该节点添加到队列中。这可能会导致您将同一节点的多个副本放入优先级队列中,这很好--当您第一次将其排出队列时,您将得到正确的距离。更新的代码如下所示:

代码语言:javascript
复制
priority_queue<Node, vector<Node>, greater<Node> > fringe;
fringe.push(initialState);

unordered_set<Node> visited;


while (true){

    if (fringe.empty()) // no solution
        return -1;

    Node current = fringe.top();
    fringe.pop();

    /* Mark the node as visited and don't visit it again if you already saw it. */
    if (visited.insert(current).second == false) continue;

    if (current.isGoal())
        return current.getDistance();

    auto successors = current.getSuccessors();

    /* Add successors to the priority queue. This might introduce duplicate nodes,
     * but that's fine. All but the lowest-priority will be ignored.
     */
    for (auto s : successors){
        fringe.push(s);
    }
}

根据后续程序的实现方式,上面的代码可能有错误,但在概念上是正确的。

希望这能有所帮助!

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21678268

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档