A*算法

启发式搜索算法

  所谓启发式搜索,就在于当前搜索结点往下一个结点搜索时候,通过一个启发函数来进行指导,选择代价最少的作为下一步搜索结点。

  DFS和BFS在展开结点时候都属于盲目型的搜索,也就是说,它不会选择哪个结点在下一次搜索中更优而去跳转到该结点进行

下一步的搜索。在运气不好的情况下,均需要试探完整个解集空间。显然,智能应用于问题规模不大的搜索问题中。

  而与DFS,BFS不同的是,一个经过仔细设计的启发函数,往往可以在很快的事件内就可以得到一个搜索问题的最优解,对于NP问题,也可以在多项式时间内得到一个较优解。是的,关键是如何设计这个启发函数。

A*搜索算法

  是启发式算法的一种,一般用于在游戏中主角或者NPC的寻路。

它的核心部分,在于一个估值函数的设计上:

  f(n) = g(n) + h(n)

其中f(n)是每个可能试探点的估值,它有两部分组成:

1. 为g(n),它表示从起始搜索点到当前点的代价(通常用某结点在搜索树中的深度来表示).

2. 为h(n), 它表示启发式搜索的最为重要的一部分,为当前结点到目标节点的估值,h(n)的设计好坏,直接影响着具有此种启发式函数的启发式算法能否称为A*算法。

  一种具有f(n) = g(n) + h(n)策略的启发式算法能成为A*算法的充分条件是:

  1. 搜索树上存在着从开始点到终点的最优路径。

  2. 问题是有限的。

  3. 所有结点的子结点的搜索代价值>0

  4. h(n) =<h*(n) (h*(n)为实际问题的代价值

当四个条件都满足时候,一个具有f(n)=g(n)+h(n)策略的启发式算法就能称为A*算法,并一定能够找到最优解。

A* 算法的核心过程在于,每次选择下一个当前搜索点时候,是从所有已经探知的但未搜索过点中(可能是不同层,亦可不在同一条支路上),选取f值最小的结点进行展开。而所有"已经探知的但未搜索过的点"可以通过一个按f值升序的队列(优先队列)进行排列。这样,在整体的搜索过程中,只要按照类似广度优先的算法框架,从优先队列中弹出队列首元素(f值),对其可能子结点计算g,h和f值,直到优先队列为空(无解)或者找到终止点为止。

A*算法和广度,深度优先和Dijkstra算法的联系在于:当g(n)=0时候,该算法类似于DFS,当h(n)=0时候,该算法类似BFS,且同时,如果h(n)为0,只要求出g(n),也就求出起点到任意顶点n的最短路径,则转化为单源最短路径问题,也就是Dijkstra算法。

 

A*算法流程

  首先将起始结点S放入OPEN表,CLOSE表置空,算法开始时:

1.如果OPEN表不为空,从表头取一个结点n,如果为空算法失败。

2.n是目标解时候,继续寻找或者终止算法。

3.将n的所有后续结点展开,就是从n可以直接关联的结点(子结点),如果不在CLOSE表中,将OPEN表按f(x)排序,最小的放在表头,重复算法,回到1. 

 

 1 //OPEN-->CLOSE,起点-->任意顶点g(n)-->目标顶点h(n)
 2 closedset := the empty set                 //已经被估算的节点集合   
 3     openset := set containing the initial node //将要被估算的节点集合
 4     g_score[start] := 0                        //g(n)
 5     h_score[start] := heuristic_estimate_of_distance(start, goal)    //h(n)
 6     f_score[start] := h_score[start]     
 7       
 8     while openset is not empty    //若OPEN表不为空
 9         x := the node in openset having the lowest f_score[] value //x为OPEN表中最小的
10         if x = goal                                               //如果x是一个解
11             return reconstruct_path(came_from,goal)             //
12         remove x from openset
13         add x to closedset                            //x放入CLSOE表
16         for each y in neighbor_nodes(x)
17             if y in closedset
18                 continue
19             tentative_g_score := g_score[x] + dist_between(x,y)
20 
21             if y not in openset
22                 add y to openset
23                 tentative_is_better := true
24             else if tentative_g_score < g_score[y]
25                 tentative_is_better := true
26             else
27                 tentative_is_better := false
28             if tentative_is_better = true
29                 came_from[y] := x
30                 g_score[y] := tentative_g_score
31                 h_score[y] := heuristic_estimate_of_distance(y, goal)  //x-->y-->goal
32                 f_score[y] := g_score[y] + h_score[y]
33     return failure
34 
35 function reconstruct_path(came_from,current_node)
36     if came_from[current_node] is set
37         p = reconstruct_path(came_from,came_from[current_node])
38         return (p + current_node)
39     else
40         return the empty path 

 

 与结点写在一起的数值表示那个结点的价值f(n),当OPEN表为空时CLOSE表中将求得从V0到其它所有结点的最短路径。

     考虑到算法性能,外循环中每次从OPEN表取一个元素,共取了n次(共n个结点),每次展开一个结点的后续结点时,需O(n)次,同时再对OPEN表做一次排序,OPEN表大小是O(n)量级的,若用快排就是O(nlogn),乘以外循环总的复杂度是O(n^2 * logn),

     如果每次不是对OPEN表进行排序,因为总是不断地有新的结点添加进来,所以不用进行排序,而是每次从OPEN表中求一个最小的,那只需要O(n)的复杂度,所以总的复杂度为O(n*n),这相当于Dijkstra算法。

 本文内容摘自v_JULY_v的A*算法

posted @ 2015-03-15 21:34  David_W  阅读(338)  评论(0编辑  收藏  举报