как преобразовать этот код из Dijkstra в Astar?

Итак, у меня есть проект, в котором я хочу перейти на Astar из-за скорости.

Но C++ не моя сильная сторона. Может ли кто-нибудь помочь мне (или сказать мне, как это сделать..), преобразовав алгоритм из Дейкстры в Астар?

Я нашел эту реализацию 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();
    }
}

Функция для поиска пути такова: Graph::findPath_r

Что я действительно хочу сделать, так это сохранить края (потому что они решают, будет ли дорога в обе стороны или в одну сторону).

Вот другие файлы: Graph.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;
}

person Community    schedule 03.07.2012    source источник
comment
На заметку: важно ли вам реализовать это самостоятельно? Библиотека Boost Graph (BGL) отлично подходит для этих целей...   -  person mensi    schedule 03.07.2012
comment
Я переписал свой плагин, используя алгоритм boost dijkstras, но он все испортил, поэтому я сделал откат ;/ теперь я хочу использовать звездный алгоритм, потому что он быстрее. Или кто-нибудь знает, как резко увеличить скорость в этом куске кода?   -  person    schedule 03.07.2012
comment
@GamErix Вы спрашиваете, как увеличить скорость. Первым шагом всегда является запуск профилировщика вашего кода. Никогда не оптимизируйте ничего до этого.   -  person Deestan    schedule 03.07.2012
comment
код расчета является многопоточным, а основной код выполняется в другом потоке, поэтому они не мешают друг другу.   -  person    schedule 03.07.2012
comment
если вам нужен полный исходный код: gpb.googlecode.com/files/   -  person    schedule 03.07.2012
comment
Чтобы использовать A-Star, вам потребуется допустимая эвристика. Если ваш график представляет физические точки (например, дорожную сеть), вы можете использовать расстояние по прямой или манхэттенское расстояние в качестве эвристики.   -  person rrjamie    schedule 05.07.2012
comment
данные графика имеют точки в форме XYZ, расстояния между каждыми узлами рассчитываются, а затем сохраняются (конечно, между подключенными узлами)   -  person    schedule 05.07.2012
comment
@GamErix: вам нужно найти const функции-члены и передать объекты функциям const T& вместо T*.   -  person Mooing Duck    schedule 05.07.2012
comment
Что такое FCost и GCost? Вероятно, нам нужно это знать, чтобы ответить на вопрос. (Поставьте описания тех, кто в вопросе, а не в комментарии)   -  person Mooing Duck    schedule 05.07.2012
comment
Я думаю, что Gcost - это общая стоимость пути до сих пор, и FCost, я думаю, такой же. Это не мой код. Я нашел код где-то в googlecode, но больше не могу его найти.   -  person    schedule 06.07.2012
comment
а нашел! : code.google.com/p/romania-pathfind   -  person    schedule 06.07.2012


Ответы (1)


Вы упомянули библиотеку в GoogleCode. Совершенно ясно, что вы хотите делать, и я думаю, что лучше всего написать свою реализацию самостоятельно.

Во-первых, вы должны знать, что Dijsktra — это частный случай A*. В A* у вас есть эвристика с именем h; A* = возможная реализация Dijsktra, когда h является нулевой функцией.

Тогда о вашей реализации, давайте начнем с 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, у вас будет алгоритм Дейсктра, а это не то, что вам нужно. Здесь мы возьмем евклидово расстояние между узлом и целью. Вычисляется медленнее, чем манхэттенское расстояние, но дает лучшие результаты. Вы можете изменить это позже.

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

Это означает, что вы должны разместить свои узлы в трехмерном пространстве. Обратите внимание, что эвристика — это эвристика, то есть оценка расстояния.

Я не буду писать код. Я напишу псевдокод, адаптированный к вашей ситуации. Исходный псевдокод находится на странице A* Википедии. Этот псевдокод — ваша 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

В приведенной выше реализации используются односторонние ребра.

Вы смогли написать алгоритм Dijsktra на C++, поэтому написание этого псевдокода на C++ не должно быть проблемой.

Вторая часть, выступления. Во-первых, измерьте ;).

У меня есть несколько советов, которые могут улучшить производительность:

  • использовать пул памяти для освобождения памяти
  • используйте навязчивый список для открытого списка (вы также можете сделать его автоматической сортировкой с помощью этой техники)

Я советую вам прочитать A* для начинающих, это полезно, даже если вы не используйте тайловую карту.

person Synxis    schedule 07.07.2012