标签:
启发式搜索算法
所谓启发式搜索,就在于当前搜索结点往下一个结点搜索时候,通过一个启发函数来进行指导,选择代价最少的作为下一步搜索结点。
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*算法
标签:
原文地址:http://www.cnblogs.com/david-wang/p/4340491.html