Live2D

C++Boost实现A*启发式算法搜索最小路径

参考链接1:https://www.cnblogs.com/xiaochi/p/8671740.html

参考链接2:http://www.mamicode.com/info-detail-2486552.html

在宽度优先和深度优先搜索里面,我们都是根据搜索的顺序依次进行搜索,可以称为盲目搜索,搜索效率非常低。

而启发式搜索则大大提高了搜索效率,由这两张图可以看出它们的差别:

 

 

(左图类似与盲搜,右图为启发式搜索

 

很明显启发式的搜索效率远远大于盲搜。

什么是启发式搜索(heuristic  search)

  利用当前与问题有关的信息作为启发式信息,这些信息是能够提升查找效率以及减少查找次数的。

如何使用这些信息,我们定义了一个估价函数 h(x) 。h(x)是对当前状态x的一个估计,表示 x状态到目标状态的距离。

有:1、h(x) >= 0 ;  2、h(x)越小表示 x 越接近目标状态; 3、如果 h(x) ==0 ,说明达到目标状态。

与问题相关的启发式信息都被计算为一定的 h(x) 的值,引入到搜索过程中。

  然而,有了启发式信息还不行,还需要起始状态到 x 状态所花的代价,我们称为 g(x) 。比如在走迷宫问题、八数码问题,我们的 g(x) 就是从起点到 x 位置花的步数 ,h(x) 就是与目标状态的曼哈顿距离或者相差的数目;在最短路径中,我们的 g(x) 就是到 x 点的权值,h(x)  就是 x 点到目标结点的最短路或直线距离。

  现在,从 h(x) 和 g(x) 的定义中不能看出,假如我们搜索依据为 F(x) 函数。

  当 F(x) = g(x) 的时候就是一个等代价搜索,完全是按照花了多少代价去搜索。比如 bfs,我们每次都是从离得近的层开始搜索,一层一层搜 ;以及dijkstra算法,也是依据每条边的代价开始选择搜索方向。 

  当F(x) = h(x) 的时候就相当于一个贪婪优先搜索。每次都是向最靠近目标的状态靠近。

  人们发现,等代价搜索虽然具有完备性,能找到最优解,但是效率太低。贪婪优先搜索不具有完备性,不一定能找到解,最坏的情况下类似于dfs。

  这时候,有人提出了A算法。令F(x) = g(x) + h(x) 。(这里的 h(x) 没有限制)。虽然提高了算法效率,但是不能保证找到最优解,不适合的 h(x)定义会导致算法找不到解。不具有完备性和最优性

  几年后有人提出了 A*算法。该算法仅仅对A算法进行了小小的修改。并证明了当估价函数满足一定条件,算法一定能找到最优解。估价函数满足一定条件的算法称为A*算法。它的限制条件是 F(x) = g(x) + h(x) 。 代价函数g(x) >0 ;h(x) 的值不大于x到目标的实际代价 h*(x) 。即定义的 h(x) 是可纳的,是乐观的

 

         不同的估价函数对算法的效率可能产生极大的影响。尤其是 h(x) 的选定,比如在接下来的八数码问题中,我们选择了曼哈顿距离之和作为 h(x) ,你也可以选择相差的格子作为 h(x),只不过搜索的次数会不同。当 h(x) 越接近 h*(x) ,那么扩展的结点越少!

 

用boost的图论库编辑代码如下:

#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include <deque>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>//A*寻路算法
using namespace std;
using namespace boost;

enum { A, B, C, D, E, N };
string Names = "ABCDE";
//定义别名,两个顶点直接连接的边
using Edge = pair<int, int>;
//创建一个图                 边     顶点    有方向   无特性            边的权重是int
using Graph = adjacency_list<listS, vecS, directedS, no_property, property<edge_weight_t, int>>;
//创建一个图
Graph make_graph()
{
    //连接的边
    vector<Edge> edges = { {A,B}, {A,C},{A,D},{B,E},{C,E},{D,E} };
    //边对应的权重
    vector<int> weight = { 3,1,4,5,2,6 };
    //创建一个图对象
    return Graph(edges.begin(), edges.end(), weight.begin(), N);
}
//创建一个结构体,用于抛出找到信息
struct found_goal
{

};
//A*要到达的目标顶点
template<class vertex>
class astar_my_visitor :public boost::default_astar_visitor
{
public:
    //初始化内置地图
    astar_my_visitor(vertex goal) :m_goal(goal)
    {

    }
    //重载examine_vertex方法
    template<class Graph>
    void examine_vertex(vertex v, Graph &g)
    {
        //如果与目标顶点一样,则说明找到
        if (v == m_goal)
        {
            //抛出抛出找到信息
            throw found_goal();
        }
    }
private:
    //目标顶点
    vertex m_goal;
};

//计算权重寻找最短路径
template<class Graph,class costtype>
class distance_heuristic :public boost::astar_heuristic<Graph, costtype>
{
public:
    //类型替换
    using Vertex = typename boost::graph_traits<Graph>::vertex_descriptor;
    //初始化
    distance_heuristic(Vertex Goal, Graph &graph):Goal_(Goal),graph_(graph)
    {
    }
    //重载()运算符 获得目标点到指定点的距离
    costtype operator()(Vertex v)
    {
        return get(vertex_index, graph_, Goal_) - get(vertex_index, graph_, v);
    }
private:
    Vertex Goal_;
    Graph &graph_;
};


int main()
{
    //创建图
    Graph myg = make_graph();

    //创建简写
    using Vertex = boost::graph_traits<Graph>::vertex_descriptor;
    using Cost = int;

    Vertex start = vertex(A, myg);//开始位置
    Vertex goal = vertex(E, myg);//结束位置

     //保存走过路径(由后向前)
    vector<Vertex>parents(boost::num_vertices(myg));
    //保存长度
    vector<Cost>distance(boost::num_vertices(myg));

    try
    {
        //求从指定点到终点的路线
        boost::astar_search_tree(myg, start, distance_heuristic<Graph, Cost>(goal, myg),//传递距离
           //求出路径,以及路径对应的权重,访问器访问                                 因为重载了()运算符
            boost::predecessor_map(&parents[0]).distance_map(&distance[0]).visitor(astar_my_visitor<Vertex>(goal))
        );
    }
    //catch信息
    catch (found_goal fg)
    {
        //要到的位置的前一个到达的位置如果是goal(下标是当前点,值是到这个点之前的点)
        if (parents[goal] == goal)
        {
            cout << "无路可走" << endl;
        }
        deque<Vertex> route;
        //顺藤摸瓜
        for (Vertex v = goal; v != start; v = parents[v])
        {
            route.push_front(v);
        }
        cout<<""<<Names[start]<<""<<Names[goal]<<"的最短路径为:"<<endl;
        cout << Names[start];
        for (auto i : route)
        {
            cout <<"->"<< Names[i];
        }
        cout<<endl;
    }
    return 0;
}

运行结果如图

posted @ 2020-02-22 11:59  檀木  阅读(627)  评论(0编辑  收藏  举报
//一下两个链接最好自己保存下来,再上传到自己的博客园的“文件”选项中