如何将此代码从 Dijkstra 转换为 Astar?

Posted

技术标签:

【中文标题】如何将此代码从 Dijkstra 转换为 Astar?【英文标题】:how to convert this code from Dijkstra to Astar? 【发布时间】:2012-07-03 17:05:26 【问题描述】:

所以我有一个项目,由于速度原因,我想切换到 Astar。

但 C++ 不是我的强项。谁能帮我(或告诉我怎么做……)将算法从 Dijkstra 转换为 Astar?

我发现了这个 Astar 实现: http://code.google.com/p/a-star-algorithm-implementation/

但我不知道如何在现有代码中使用它。

这是得到算法的图形文件:

#include "Graph.h"
#include <iostream>
#include <algorithm>
#include <stack>

Graph::Graph(void)



Graph::~Graph(void)

    while(!mNodes.empty())
    
        delete mNodes.back();
        mNodes.pop_back();
    


void Graph::addNode(int name, bool exists, Node** NodeID )

    Node* pStart = NULL;
    mNodes.push_back(new Node(name,exists));
    std::vector<Node*>::iterator itr;
    itr = mNodes.begin()+mNodes.size()-1;
    pStart = (*itr);
    if(exists == true)pStart->DoesExist_yes();
    *NodeID = pStart;


void Graph::connect_oneway(Node* pFirst, Node* pSecond, int moveCost)

    if(pFirst != NULL && pSecond != NULL)
    
        pFirst->createEdge(pSecond, moveCost);
    


#define MAX_NODES (32768)
#define MAX_CONNECTIONS (5)
#include <time.h>

int * Graph::findPath_r(Node* pStart, Node* pEnd)

    int *arr = new int[MAX_NODES+2];

    for (int i=0; i<MAX_NODES; i++)
        arr[i] = -1;

    arr[0] = 0;
    if(pStart == pEnd)
    
        return arr;
    

    std::vector<Node*> openList;
    openList.push_back(pStart);
    Node* pCurrNode = NULL;


    while(!openList.empty())
    
        //Get best node from open list (lowest F value).
        //Since we sort the list at the end of the previous loop we know
        //the front node is the best
        pCurrNode = openList.front();

        //Exit if we're are the goal
        if(pCurrNode == pEnd)
            break;

        //Remove the node from the open list and place it in the closed
        openList.erase(openList.begin());
        pCurrNode->setClosed(true); //We use a flag instead of a list for speed
        //Test all of the edge nodes from the current node
        std::vector<Edge*>* pEdges = pCurrNode->getEdges();
        Node* pEdgeNode = NULL;
        for(std::vector<Edge*>::iterator i = pEdges->begin(); i != pEdges->end(); ++i)
        
            pEdgeNode = (*i)->pNode;
            //If it's closed we've already analysed it
            if(!pEdgeNode->getClosed() && pCurrNode->DoesExist() == true)
            
                if(!inList(pEdgeNode,&openList))
                
                    openList.push_back(pEdgeNode);
                    pEdgeNode->setGCost(pCurrNode->getGCost()+(*i)->moveCost);
                    pEdgeNode->calcFCost();
                    pEdgeNode->setParent(pCurrNode);
                
                else
                
                    //If this is a better node (lower G cost)
                    if(pEdgeNode->getGCost() > pCurrNode->getGCost()+(*i)->moveCost)
                    
                        pEdgeNode->setGCost(pCurrNode->getGCost()+(*i)->moveCost);
                        pEdgeNode->calcFCost();
                        pEdgeNode->setParent(pCurrNode);
                    
                
            
        
        //Place the lowest F cost item in the open list at the top, so we can
        //access it easily next iteration
        std::sort(openList.begin(), openList.end(),  Graph::compareNodes);
    
    //Make sure we actually found a path
    if(pEnd->getParent() != NULL)
    
        //Output the path
        //Use a stack because it is LIFO
        std::stack<Node*> path;
        while(pCurrNode != NULL)
        
            path.push(pCurrNode);
            pCurrNode = pCurrNode->getParent();
        

        int counter = 0;
        arr[1] = 0;
        while(!path.empty())
        
            arr[counter+2] = path.top()->getName();
            counter++;
            arr[1] += path.top()->getGCost();
            path.pop();
        
        arr[0] = counter;
        return arr;
    
    return arr;


bool Graph::inList(Node* pNode, std::vector<Node*>* pList)

    for(std::vector<Node*>::iterator i = pList->begin(); i != pList->end(); ++i)
    
        if((*i) == pNode)
        
            return true;
        
    

    return false;


bool Graph::compareNodes(Node* pFirst, Node* pSecond)

    return pFirst->getFCost() < pSecond->getFCost();


void Graph::reset(void)

    for(std::vector<Node*>::iterator i = mNodes.begin(); i != mNodes.end(); ++i)
    
        (*i)->reset();
    

查找路径的函数是这个: 图::findPath_r

我真正想做的是保留边缘(因为它们决定道路是双向还是单向)。

以下是其他文件: 图.h

#ifndef _GRAPH_H_
#define _GRAPH_H

#include "Node.h"

class Graph

public:
    Graph(void);
    ~Graph(void);

    //void addNode(int name, bool exists);
    void addNode(int name, bool exists, Node** NodeID );
    void connect_oneway(int ppFirst, int ppSecond, int moveCost);
    void connect_oneway(Node* pFirst, Node* pSecond, int moveCost);
    //int * findPath_r(int start, int end);
    int * findPath_r(Node* pStart, Node* pEnd);
    void reset(void);
private:
    void findNodesx(int firstName, Node** ppFirstNode);
    bool inList(Node* pNode, std::vector<Node*>* pList);
    static bool compareNodes(Node* pFirst, Node* pSecond);
    std::vector<Node*> mNodes;
;

#endif

节点.h

#ifndef _NODE_H_
#define _NODE_H_

#include <string>
#include <vector>

//Forward declare Node so Edge can see it
class Node;

struct Edge

    Edge(Node* node, int cost) : pNode(node), moveCost(cost)
    Node* pNode;
    int moveCost;
;

class Node

public:
    Node(void);
    Node(int name, bool exists);
    ~Node(void);

    void createEdge(Node* pTarget, int moveCost);

    void setGCost(int cost);
    void setClosed(bool closed);
    void setParent(Node* pParent);

    int getGCost(void);
    int getFCost(void);
    bool getClosed(void);
    Node* getParent(void);
    int getName(void);
    bool DoesExist(void);
    bool DoesExist_yes(void);
    std::vector<Edge*>* getEdges(void);

    void calcFCost(void);
    void reset(void);
private:
    int mGCost;
    int mTotal;
    bool mClosed;
    Node* mpParent;
    int mName;
    bool mHeur;
    std::vector<Edge*> mEdges;
;

#endif

节点.cpp

#include "Node.h"

Node::Node(void)



Node::Node(/*const std::string&*/int name, bool exists) : mGCost(0), mTotal(0), mClosed(false), mpParent(NULL), mName(name), mHeur(exists)



Node::~Node(void)

    while(!mEdges.empty())
    
        delete mEdges.back();
        mEdges.pop_back();
    


int Node::getName(void)

    return mName;


void Node::createEdge(Node* pTarget, int moveCost)

    mEdges.push_back(new Edge(pTarget, moveCost));


void Node::setClosed(bool closed)

    mClosed = closed;


bool Node::getClosed(void)

    return mClosed;


std::vector<Edge*>* Node::getEdges(void)

    return &mEdges;


int Node::getGCost(void)

    return mGCost;


void Node::setGCost(int cost)

    mGCost = cost;


void Node::calcFCost(void)

    mTotal = mGCost;


void Node::setParent(Node* pParent)

    mpParent = pParent;


int Node::getFCost(void)

    return mTotal;


bool Node::DoesExist(void)

    return mHeur;


bool Node::DoesExist_yes(void)

    mHeur = true;
    return true;


Node* Node::getParent(void)

    return mpParent;


void Node::reset(void)

    mGCost = 0;
    mTotal = 0;
    mClosed = false;
    mpParent = NULL;

【问题讨论】:

附带说明:您自己实现它重要吗? Boost Graph Library (BGL) 非常适合这些事情... 我用 boost dijkstras 算法重写了我的插件,但它搞砸了所有的东西,所以我做了一个回滚;/现在我想使用星型算法,因为它更快。或者有谁知道如何显着提高这段代码的速度? @GamErix 您询问如何提高速度。第一步始终是在您的代码上运行分析器。在此之前不要费心优化任何东西 计算代码是线程化的,主代码在另一个线程中运行,所以互不干扰。 要使用 A-Star,您需要Admissible heuristic。如果您的图表表示物理点(例如,道路网络),您可以使用直线距离或曼哈顿距离作为启发式方法。 【参考方案1】:

您提到了 GoogleCode 上的一个库。节点清楚你想要做什么,我认为最好的办法是自己编写你的实现。

首先,您应该知道 Dijsktra 是 A* 的一个特例。在 A* 中,您有一个启发式方法,名为 h; A* = 当 h 为空函数时 Dijsktra 的可能实现。

那么,关于你的实现,让我们从Node 开始。它将需要以下功能:

constructor, destructor
create/get edge
set/get parent
set/is closed (for speed)
set/get GCost
set/get FCost
set/is obstacle (name way more descriptive than 'DoesExist')
set/get position
reset

// optional method:
get name

希望您的这部分代码不会有太大变化。启发式代码将放置在路径查找器中。 Edge 类保持不变。

现在是大号:Graph。您无需删除任何公共方法。

您将需要一种启发式方法。对于将要描述的实现,您将需要一个可接受的一致启发式:

不得高估到球门的距离(可接受) 必须是单调的(一致的)

一般情况下的签名是int getHCost(Node* node);。如果你总是返回 0,你会得到一个 Dijsktra 算法,这不是你想要的。在这里,我们将采用节点和目标之间的欧几里得距离。计算速度比曼哈顿距离慢,但结果更好。您可以在之后更改此设置。

int getHCost(Node* node, Note* goal);

这意味着您必须将节点放置在 3d 空间中。请注意,启发式是启发式,即距离的估计

我不会写代码。我会写一些适合你情况的伪代码。原始伪代码位于Wikipedia A* page。这个伪代码是你的findPath_r 函数:

function A*(start,goal)
    set all nodes to not closed // The set of nodes already evaluated.
    openset = start    // The set of tentative nodes to be evaluated, initially containing the start node

    start.gcost = 0    // Cost from start along best known path.
    // Estimated total cost from start to goal through y.
    start.fcost = start.gcost + getHCost(start, goal)

    while openset is not empty
        current = the node in openset having the lowest f_cost (usually the first if you use a sorted list)
        if current == goal
            return construct_path(goal)

        remove current from openset
        current.closed = true
        for each neighbor in (node connected by edge in current.edges) // Here is the condition for one-way edges
            if neighbor.closed or neighbor.obstacle
                continue

            gcost = current.gcost + dist_between(current,neighbor) // via edge distance

            if neighbor not in openset
                add neighbor to openset
                neighbor.parent = current
                neighbor.gcost = gcost
                neighbor.fcost = neighbor.gcost + getHCost(neighbor, goal)
            else if gcost < neighbor.gcost
                neighbor.parent = current
                neighbor.gcost = gcost
                neighbor.fcost = neighbor.gcost + getHCost(neighbor, goal)
                update neighbor position in openset

    return failure

function construct_path(current_node)
    std::vector<Node*> path
    while current_node != 0
        path.push_front(current_node)
        current_node = current_node.parent
    return path

上面的实现使用单向边。

你可以用 C++ 编写 Dijsktra 算法,所以用 C++ 编写这个伪代码应该不成问题。

第二部分,表演。首先,测量;)。

我有一些提示可以提高性能:

使用内存池进行分配解除分配 对打开列表使用侵入式列表(您也可以使用此技术使其自动排序)

我建议你阅读A* for beginners,这是一个有用的阅读,即使你不使用 tilemap。

【讨论】:

以上是关于如何将此代码从 Dijkstra 转换为 Astar?的主要内容,如果未能解决你的问题,请参考以下文章

如何将此代码从 Intel(nasm) 转换为 AT&T(gas) 语法?

如何将此代码转换为 PyQt5?

Vue-Select:如何将此 fetch() 代码转换为使用 axios?

如何将此 SetLike 集合从 Scala 2.12 转换为 2.13?

我想将此代码从 SAS 转换为 ECL(HPCC)

如何将此 Java 代码转换为 kotlin 代码