【问题标题】:A* search in C: implementation and performance on graph mapC中的A*搜索:图上的实现和性能
【发布时间】:2014-12-15 23:08:03
【问题描述】:

我正在为使用图形的“隐式”表示的课程编写“游戏”项目:我有一个 N*M 项的矩阵,定义如下:。

typedef struct {
    int x;
    int y;
    int k;      //value of the position
    int ID;
    bool path;  //temporary field used to print the path
} VCOORD;

typedef struct gObj {
    //numero vertici
    int             vNum;           //number of vertices

    VCOORD  **      maze;           //pointer to implicit maze
    int             width;          //width of the maze
    int             height;         //height of the maze

    //function pointers
    struct gObj *   ( *build)           ( struct gObj *, int );
    Set *           ( *getAdjList )     ( struct gObj *graph, int u );

    void            ( *insertEdge )     ( struct gObj *, int , int , int  );
    void            ( *insertVertex )   ( struct gObj *graph );

    int             ( *getWeight )      ( struct gObj *, int, int );
    int             ( *transpose )      ( struct gObj *graph );
    int         *   ( *path)            ( struct gObj *, int , int );
} GRAPHOBJ;

我已经实现了:

BFS、Dijkstra、A*

BFS 和 Dijkstra 工作正常,但我对 A* 有一些问题

A*(一颗星)

int *a_star ( GRAPHOBJ *graph, int s, int t ) {
    int x, y, i;
    int cost;
    bool found = false;
    void    *   tmp;                    //used to store Heap first element
    Set     *   AdjList     = NULL;     //Adjiacence list implemented as queue
    Set     *   closed      = NULL;     //Closed set implemented as queue
    Heap    *   open        = NULL;     //Open set implemented as Heap
    int     *   pred        = ( int *) malloc ( graph->vNum * sizeof ( int ) );

    int g[graph->vNum];                 //g distance function
    int f[graph->vNum];                 //f distance function

    //initialization
    for ( i = 0; i < graph->vNum; i++ ) {
        f[i]        = INFINITE;
        g[i]        = 0;
        pred[i]     = NIL;
    }
    f[s] = heuristic ( graph, s, t );
    open = initializeHeap ( minHeapify );
    insert ( open, new_HeapData ( s, 0 ) );

    while ( !isHeapEmpty ( open ) && found == false ) {

        //extracting the first Object of the Heap ( Open )
        tmp     = extractFirst ( open );
        //pushing the node into the Closed set
        push ( closed , setInt ( x ) );
        //get the int number from the extracted Object
        x       = getData ( tmp );
        //get the ditance f from the extracted Object
        f[x]    = getKey ( tmp );

        //debug
        if ( PRINTALL ) graph->maze[getCoord ( graph, x )->y][getCoord ( graph, x )->x].path = true;
        //printf ("x: %d ", x);
        if ( x == t ) {
            found = true;
        } else {

            AdjList = graph->getAdjList ( graph, x );

            while ( !isEmpty ( AdjList ) ) {
                //getting the first element of the adj. list
                y               = getInt ( getFront ( AdjList ) );
                //graph->getWeight refers to getMatrixWeight
                g[y]            = g[x] + graph->getWeight ( graph, x, y );
                cost            = g[y] + heuristic ( graph, y, t );

                //checking if y is in the open set
                bool yInOpen    = heapIntSearch ( open, y );
                //checking if y is in the closed set
                bool yInClosed  = intSearch ( closed , y );

                if ( yInOpen && cost < f[y] ) { // case 1
                    decreaseKey ( open, y, cost );
                } 

                else if (  yInClosed && cost < f[y] ) { // case 2
                    deleteFromSet ( closed, y );
                    insert ( open, new_HeapData ( y, cost ) );
                }

                else if ( !yInClosed && !yInOpen ) { // case 3
                    insert ( open, new_HeapData ( y, cost ) );
                }
                AdjList = dequeue ( AdjList );
                if ( pred[y] == NIL )
                    pred[y] = x;
            }
        }           
    }
    pred[s] = NIL;
    printf ("\n\n");
    return pred;
}

启发式函数,欧几里得/曼哈顿距离:

int heuristic ( GRAPHOBJ *graph, int s, int t ) {
    VCOORD *    start       = getCoord ( graph, s );
    VCOORD *    target      = getCoord ( graph, t );
    /*manhattan*/return ( abs ( start->x - target->x ) + abs ( start->y - target->y ) );
    /*euclidian*///return pow ( pow ( ( target->x - start->x ), 2 ) + pow ( ( target->y - start->y ), 2 ), 0.5 );
}

边权重函数:

int getMatrixWeight ( GRAPHOBJ *graph, int u, int v ) {
    int weight = 1;
    return weight;
}

Adjiacence 列表实现(NORTH、SOUTH 等是减少/增加坐标的宏:

Set *getAdjList ( struct gObj *graph, int u ) {
    Set *Adj = NULL;
    int x = u % graph->width;
    int y = u / graph->width;
    int neighbor;

    if ( NORTH(y) > 0 && graph->maze[NORTH(y)][x].k != 9 ) {
    //if ( NORTH(y) > 0 ) {
        neighbor = graph->width * ( NORTH(y) ) + ( x );
        Adj = enqueue ( Adj, setInt ( neighbor ) );
    }
    if ( EAST(x) < graph->width && graph->maze[y][EAST(x)].k != 9  ) {
    //if ( EAST(x) < graph->width  ) {
        neighbor = graph->width * ( y ) + ( EAST(x) );      
        Adj = enqueue ( Adj, setInt ( neighbor ) );
    }
    if ( SOUTH(y) < graph->height && graph->maze[SOUTH(y)][x].k != 9 ) {
    //if ( SOUTH(y) < graph->height ) {
        neighbor = graph->width * ( SOUTH(y) ) + ( x );     
        Adj = enqueue ( Adj, setInt ( neighbor ) );
    }
    if ( WEST(x) > 0 && graph->maze[y][WEST(x)].k != 9 ) {
    //if ( WEST(x) > 0 ) {
        neighbor = graph->width * ( y ) + ( WEST(x) );      
        Adj = enqueue ( Adj, setInt ( neighbor ) );
    }

    return Adj;
}

问题:

  1. 如果边的权重等于 1,正如在 getMatrixWeight 函数中声明的那样,A* 会采用奇怪的路径,例如:http://i.imgur.com/vbGNskF.png 如果边的权重 > 1,则 A* 正常工作(不是一直,而是 99%)

  2. 如果无法到达目标,A* 似乎会进入循环。 BFS 和 Dijkstra 没问题。

  3. 我实现了一个小“实用程序”来监控脚本的性能。 BFS 和 Dijkstra 工作正常(BFS 比 Dijkstra 快一点,但它可能取决于堆实现,我认为)。 但从图中可以看出,A* 似乎慢了 10 到 30 倍!所以我认为实现有问题,但我找不到问题所在(可能与第2点:循环有关)!

我认为其他功能(集合和堆实现、邻居实现)工作正常,因为 BFS 和 Dijkstra 没有问题。

如果您想查看其余代码,请点击此处:repo:https://bitbucket.org/ozeta86/labirinto/src

【问题讨论】:

    标签: c performance dictionary graph a-star


    【解决方案1】:

    首先你有一个初始化问题,因为你 push ( closed , setInt ( x ) ); 但 x 在你第一次这样做时没有初始化。但这不是问题的原因。

    阅读您的代码,我了解到您对 A* 的实现如下:

    • f[i] 是节点 i 的最佳估计成本。
    • g[i]是通往节点i的路径的累积距离
    • 您通过添加额外的权重计算 g[y],即从 x 到 y 的路径的延长。
    • 然后,通过使用路径的累积距离加上到目标的启发式剩余距离来计算该成本。

    问题在于您的 g[] 不是累积一条路径的距离,而是累积已探索的所有路径的距离。如果多个路径使用同一个节点,你将得到一个无意义的 g。

    换句话说,对于任何到达 i 你尝试扩展的路径,g[i] 将不包含到 i 的路径的唯一距离(需要 A* 以计算有意义的成本)但不可预测的值这取决于之前探索的路径。

    因此产生了不稳定的结果。

    您必须更改数据结构,以便将路径的距离与路径一起存储在堆上,以便下次尝试扩展它时,您将使用与该路径相对应的实际距离。

    【讨论】:

    • 嗨@Christophe,我修复了推线,我有点理解这个问题,但我没有完全理解你的意思是将你的路径距离与你的堆一起存储路径。你能解释更多吗?
    • 对不起,不在堆上,我的意思是在排序队列中。当您插入时,您还必须存储距离,以便在下一个路径扩展时准确计算距离
    • 如果您不想将 g 与路径一起保存,则可以计算一个 tentative_g 并仅在 g 较小时才更新它。第二种方法的伪代码可以在英文维基百科文章中找到。
    • 我不明白...更新 g 如果更小那不是我在第一个 If 案例中所做的? if ( yInOpen &amp;&amp; cost &lt; f[y] ) { // case 1 decreaseKey ( open, y, cost ); }
    • 在 while 循环过程中邻接列表中,您肯定是一个无条件的 g[y]=g[x]+...
    【解决方案2】:

    我使用了另一种实现,与 BFS 和 dijkstra 相比,它现在可以工作并且具有良好的性能。我用Introduction to A* from Red Blob Games

    int *a_star ( GRAPHOBJ *graph, int s, int t ) {
    
        int i;
        int current;
        bool found              = false;
        void    *   tmp;                    //used to store Heap first element
        Set     *   AdjList     = NULL;     //Adjiacence list implemented as queue
        Heap    *   frontier    = NULL;     //Open set implemented as Heap
        frontier                = initializeHeap ( minHeapify );
        int     *   came_from   = ( int *) malloc ( graph->vNum * sizeof ( int ) );
        int     *   cost_so_far = ( int *) malloc ( graph->vNum * sizeof ( int ) );
    
    
        //initialization
        for ( i = 0; i < graph->vNum; i++ ) {
            came_from[i]    = NIL;
            cost_so_far[i]  = INFINITE;
        }
        insert ( frontier, new_HeapData ( s, 0 ) );
        cost_so_far[s]  = 0;
        while ( !isHeapEmpty ( frontier ) ) {
            tmp     = extractFirst ( frontier );
            current = getData ( tmp );
    
    
            if ( current == t ) {
                break;
            }
            AdjList = graph->getAdjList ( graph, current );
            while ( !isEmpty ( AdjList ) ) {
                int y = getInt ( getFront ( AdjList ) );
                AdjList = dequeue ( AdjList );
                int new_cost = cost_so_far[current] + graph->getWeight ( graph, current, y );
                if ( cost_so_far[y] == INFINITE || new_cost < cost_so_far[y] ) {
                    cost_so_far[y] = new_cost;
                    int priority = new_cost + heuristic ( graph, y, t );
                    insert ( frontier, new_HeapData ( y, priority ) );
                    came_from[y] = current;
                }
            }
        }
        return came_from;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多