GBFS,Dijkstra,A*路径规划比较与C++实例

一家之言,仅作分享,如有不合理或需要改进的地方,欢迎各位讨论。

图搜索的基本流程

  1. 创建一个容器,一般称为openlist,用来存储将要访问的节点
  2. 将起点加入容器开始循环:
  3. ---- 弹出:从容器中取出一个节点
  4. ---- 扩展:获取该节点周围的节点,将这些节点放入容器

一、贪婪最佳优先算法(Greedy Best First Search, GBFS)

GBFS中采用的openlist是一种优先队列(Priority Queue),在优先队列中元素被赋予了优先级,最高优先级元素优先删除。这里的优先级高低判断在路径规划中就是代价值。
在图搜索算法中,使用代价函数 f ( n ) f(n) f(n)来作为优先级判断的标准, f ( n ) f(n) f(n)越小,优先级越高,反之优先级越低。GBFS作为一种启发式搜索算法,使用启发评估函数 h ( n ) h(n) h(n)来作为代价函数,也就是 f ( n ) = h ( n ) f(n)=h(n) f(n)=h(n),其中 h ( n ) h(n) h(n)当前节点到终点的代价,它可以指引搜索算法往终点靠近,主要用欧氏距离(Euclidean Distance)或者曼哈顿距离(Manhattan Distance)来表示。
在这里插入图片描述优点:运行速度快。
缺点:不一定能找到最短路径。问题在于GBFS是基于贪心策略的, 它试图向目标移动尽管这不是正确的路径。 由于它仅仅考虑到达目标的代价, 而忽略了当前已花费的代价, 于是尽管路径变得很长, 它仍然继续走下去。

二、Dijkstra算法

Dijkstra算法与GBFS算法使用了一样的openlist,但是不同点在于Dijkstra算法的代价函数是 f ( n ) = g ( n ) f(n)=g(n) f(n)=g(n),其中 g ( n ) g(n) g(n)从起始点到当前点的代价,它可以确保可以找到距离最短的路径。这里的代价计算不同于上述 h ( n ) h(n) h(n)(当前点到终点的曼哈顿距离或欧式距离),而是有权重的,在实际路径规划中,这里的权重可以是每条道路的长度,拓扑中的转向关系,道路属性等等。
优点:能够找到最短路径。
缺点:检查节点数目多,效率低。

三、A*算法

A*算法是将上述两者算法进行了融合,即代价函数是 f ( n ) = g ( n ) + h ( n ) f(n)=g(n)+h(n) f(n)=g(n)+h(n),其中其中 g ( n ) g(n) g(n)起点到当前扩展节点的移动代价函数 h ( n ) h(n) h(n)是启发函数,当前节点到目标点的距离代价函数。这样可以同时具有**(1)运行效率高,(2)找到最短路径**两个优点。
两个list
o p e n l i s t openlist openlist:存放被检查来寻找最短路径的节点列表。
c l o s e l i s t closelist closelist:存放不会再被检查的节点列表。
两个代价函数
g ( n ) g(n) g(n):从起始点到当前节点的代价,例如所有途径节点的道路长度和,转向权重,道路属性权重等。
h ( n ) h(n) h(n):从当前节点到终点的代价,例如当前节点与终点的曼哈顿距离或欧式距离。
步骤
1、从起点S开始,把S作为一个等待检查的节点,放入到 o p e n l i s t openlist openlist中。
2、寻找S节点的拓出节点,将它们放入到 o p e n l i s t openlist openlist中,并设置它们的父节点为S。
3、从 o p e n l i s t openlist openlist中删除起点S,并将其放入到 c l o s e l i s t closelist closelist中。
4、计算 o p e n l i s t openlist openlist中每个节点的 f ( n ) f(n) f(n)值。
5、从 o p e n l i s t openlist openlist中选择 f ( n ) f(n) f(n)值最小的节点A,将其从 o p e n l i s t openlist openlist中删除,并放入到 c l o s e l i s t closelist closelist中。
6、检查节点A的拓出节点:
       a、 c l o s e l i s t closelist closelist中的节点不考虑;
       b、如果节点不在 o p e n l i s t openlist openlist中,则放入到 o p e n l i s t openlist openlist中,并计算此节点的 f ( n ) f(n) f(n)值,并设置其父节点为A;
       c、如果某拓出的节点B已经在 o p e n l i s t openlist openlist中,计算新的路径从S到B(即经过A的路径)
             判断是否需要更新: g ( n ) g(n) g(n)值是否更小
             如果新的 g ( n ) g(n) g(n)值更小,则修改父节点为A,重新计算 f ( n ) f(n) f(n)值, h ( n ) h(n) h(n)值不用改变,因为节点到终点的预计代价是固定不变的;
             如果新的 g ( n ) g(n) g(n)值更大,则说明新的路径代价更高,则值不做改变( g ( n ) g(n) g(n)值不变也不更新)。
7、继续从openlist中找出 f ( n ) f(n) f(n)值最小的节点,从 o p e n l i s t openlist openlist中删除,放入到 c l o s e l i s t closelist closelist中,再继续检查其拓出节点,如此循环
8、结束条件
       a、当 o p e n l i s t openlist openlist中出现终点节点E时,说明路径已经找到;
       b、当 o p e n l i s t openlist openlist中没有了数据,则说明没有合适的路径。

四、C++代码

伪代码

初始化两个空的列表openlist和closelist,将起点加入openlist中,并设置代价值为0while(1)
{
    if(openlist != null)
    {
         从openlist中选取代价值最小的节点n.
         1. 如果节点n为终点,则从终点开始逐步追踪parent节点,一直达到起点,返回找到的结果路径,
            跳出循环,算法结束,break;
         2. 如果节点n不是终点,
            2.1 将节点n从openlist中删除,加入到closelist中.
            2.2 遍历节点n的8个相邻无碰撞节点
                2.2.1 如果相邻节点在closelist中,则跳过该节点,计算下一个节点。
                2.2.2 如果相邻节点不在openlist中,则设置该节点父节点为n,通过f(n)计算该节点的
                      代价值,并将该节点放入openlist中。
                2.2.3 如果相邻节点在openlist中,则重新计算g(n)值,判断是否更新:如果更小,则重新计算f(n);
                      反之,不做更新。 
     }
     else
     {
         break; //不能找到一条最优路径
     }
}

下面是头文件中需要定义的搜索节点结构体二元堆的类

struct SearchNode
{
    RoadInfo stRoadinfo;

    //cost of Current Node
    int iCost;

    //All estimates of node f(n) = g(n) + h(n)
    int iAllEstimateCost;

    //The local optimum value of node A*->g(n),the actual distance from the starting point to that point
    int iPartOptimalCost;

    //Estimated h(n) Spherical Distance or Manhattan Distance of Node
    int iPartEstmateCost;

    //ForwardSearchNode
    int iForwardRoadid;

    SearchNode() {
        iCost = 0;
        iAllEstimateCost = 0;
        iPartOptimalCost = 0;
        iPartEstmateCost = 0;
        iForwardRoadid = 0;
    }
};

struct CmpSearchNode{
    bool operator()(IN const SearchNode& a, IN const SearchNode& b)
    {
        return a.iAllEstimateCost < b.iAllEstimateCost;
    }
};

class RHeap{
public:
    RHeap()
    {
        std::make_heap(m_pData.begin(),m_pData.end(),CmpSearchNode());
    }

    ~RHeap(){
        m_pData.clear();
        std::vector<SearchNode>().swap(m_pData);
    }

    void push(IN SearchNode& node)
    {
        m_pData.emplace_back(node);
        std::push_heap(m_pData.begin(),m_pData.end(),CmpSearchNode());
        std::sort_heap(m_pData.begin(),m_pData.end(),CmpSearchNode());
    }

    void GetMin(OUT SearchNode& node)
    {
        std::pop_heap(m_pData.begin(),m_pData.end(),CmpSearchNode());
        node = m_pData.back();
        m_pData.pop_back();
        std::sort_heap(m_pData.begin(),m_pData.end(),CmpSearchNode());
    }

    std::size_t GetSize()
    {
        return m_pData.size();
    }

    void sort()
    {
        std::sort_heap(m_pData.begin(),m_pData.end(),CmpSearchNode());
    }

private:
    std::vector<SearchNode> m_pData;
};

下面代码是作者自定义的道路路网数据,其中包括拓扑关系,道路长度,以及转向关系,这些都是影响 g ( n ) g(n) g(n)的代价值。

void Astar_RoutePlan::AStar_Strategy(IN const SearchNode src_node, IN const SearchNode dest_node,
                                     OUT std::vector<int> &retRoadlist)
{
    retRoadlist.clear();
    RHeap openlist;
    std::list<SearchNode> closelist;
    std::map<int, SearchNode> map_node;
    std::map<int, SearchNode> map_nodeclose;

    if(src_node.stRoadinfo.id == dest_node.stRoadinfo.id)
    {
        retRoadlist.emplace_back(src_node.stRoadinfo.id);
        return;
    }

    SearchNode node = src_node;
    node.iForwardRoadid = -1;
    openlist.push(node);
    map_node.insert(std::make_pair(node.stRoadinfo.id, node));

    bool bFound = false;

    SearchNode rp_curNode;

    while(openlist.GetSize())
    {
        // Get the node with min cost f(n).
        openlist.GetMin(rp_curNode);
        std::map<int, SearchNode>::iterator iter_map_node = map_node.find(rp_curNode.stRoadinfo.id);

        if(iter_map_node != map_node.end())
        {
            map_node.erase(iter_map_node);
        }

        // Find Destination
        if(rp_curNode.stRoadinfo.id == dest_node.stRoadinfo.id)
        {
            bFound = true;
        }

        if(rp_curNode.iForwardRoadid == dest_node.stRoadinfo.id)
        {
            bFound = true;
        }

        // Get BackWard Adjacent
        std::vector<SearchNode> adjacentNodelist;
        getBackwardAdjacentNodes(rp_curNode,adjacentNodelist);

        closelist.emplace_back(rp_curNode);
        map_nodeclose.insert(std::make_pair(rp_curNode.stRoadinfo.id, rp_curNode));

        for(SearchNode tmpAdjacentNode:adjacentNodelist)
        {
            tmpAdjacentNode.iPartEstmateCost = HeuristicCost(tmpAdjacentNode,dest_node);
            tmpAdjacentNode.iPartOptimalCost = rp_curNode.iPartOptimalCost + tmpAdjacentNode.iCost;
            tmpAdjacentNode.iAllEstimateCost = tmpAdjacentNode.iPartEstmateCost + tmpAdjacentNode.iPartOptimalCost;

            if(tmpAdjacentNode.iForwardRoadid == dest_node.stRoadinfo.id)
            {
                bFound = true;
            }

            iter_map_node = map_node.find(tmpAdjacentNode.stRoadinfo.id);
            if(iter_map_node != map_node.end())
            {
                if(tmpAdjacentNode.iAllEstimateCost < iter_map_node->second.iAllEstimateCost)
                {
                    iter_map_node->second.iAllEstimateCost = tmpAdjacentNode.iAllEstimateCost;
                    iter_map_node->second.iPartEstmateCost = tmpAdjacentNode.iPartEstmateCost;
                    iter_map_node->second.iPartOptimalCost = tmpAdjacentNode.iPartOptimalCost;
                    iter_map_node->second.iForwardRoadid   = tmpAdjacentNode.iForwardRoadid;
                    openlist.sort();
                }
            }
            else if(map_nodeclose.find(tmpAdjacentNode.stRoadinfo.id)!= map_nodeclose.end())
            {
                iter_map_node->second.iAllEstimateCost = tmpAdjacentNode.iAllEstimateCost;
                iter_map_node->second.iPartEstmateCost = tmpAdjacentNode.iPartEstmateCost;
                iter_map_node->second.iPartOptimalCost = tmpAdjacentNode.iPartOptimalCost;
                iter_map_node->second.iForwardRoadid   = tmpAdjacentNode.iForwardRoadid;
            }
            else
            {
                openlist.push(tmpAdjacentNode);
                map_node.insert(std::make_pair(tmpAdjacentNode.stRoadinfo.id, tmpAdjacentNode));
            }

        }
        if(bFound || closelist.size() > 500)  //Calc time >500 then break
        {
            break;
        }
    }

    if(bFound)
    {
        while(rp_curNode.iForwardRoadid != -1)
        {
            retRoadlist.emplace_back(rp_curNode.stRoadinfo.id);
            std::map<int, SearchNode>::iterator iter_nodeclose = map_nodeclose.find(rp_curNode.iForwardRoadid);
            rp_curNode = iter_nodeclose->second;
        }
        retRoadlist.emplace_back(rp_curNode.stRoadinfo.id);
        reverse(retRoadlist.begin(), retRoadlist.end());
    }
}

void Astar_RoutePlan::getBackwardAdjacentNodes(IN const SearchNode cur_node, OUT std::vector<SearchNode> &retAdjacentNodes)
{
    retAdjacentNodes.clear();
    std::multimap< int, std::pair<int,TurnInfo> >  topoinfo;
    mParse.GetTopoInfo(topoinfo);

    std::multimap< int, std::pair<int,TurnInfo> >::iterator iter_adjacent = topoinfo.find(cur_node.stRoadinfo.id);

    if(iter_adjacent == topoinfo.end())
    {
        return;
    }

    for(size_t i=0; i<topoinfo.count(cur_node.stRoadinfo.id); i++, iter_adjacent++)
    {
        RoadInfo adjacentRoadinfo = mParse.GetRoad(iter_adjacent->second.first);
        SearchNode adjacentNode;
        if(adjacentRoadinfo.length > 0)
        {
            adjacentNode.stRoadinfo = adjacentRoadinfo;

            adjacentNode.iCost = adjacentNode.stRoadinfo.length;

            if(iter_adjacent->second.second == TURN_LEFT)
            {
                adjacentNode.iCost += 40;
            }
            else if(iter_adjacent->second.second == TURN_RIGHT)
            {
                adjacentNode.iCost += 20;
            }
            else if(iter_adjacent->second.second == GO_STRAIGHT)
            {
                adjacentNode.iCost += 0;
            }
            double dx = adjacentNode.stRoadinfo.startPoint.x - cur_node.stRoadinfo.endPoint.x;
            double dy = adjacentNode.stRoadinfo.startPoint.y - cur_node.stRoadinfo.endPoint.y;
            double abs = std::hypot(dx,dy);
            adjacentNode.iCost += abs;
            adjacentNode.iForwardRoadid = cur_node.stRoadinfo.id;
            retAdjacentNodes.emplace_back(adjacentNode);
        }
    }
}

double Astar_RoutePlan::HeuristicCost(IN const SearchNode node, IN const SearchNode dest_node)
{
    return std::fabs(node.stRoadinfo.startPoint.x - dest_node.stRoadinfo.endPoint.x)
            + fabs(node.stRoadinfo.startPoint.y - dest_node.stRoadinfo.endPoint.y);
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值