A*算法在求最短路上的应用

之前发现自己对A*的理解存在问题,在此重新更新一下博文

此处借鉴了一篇神文。

http://www.redblobgames.com/pathfinding/a-star/implementation.html

我也顺便意识到了python代码是一个比伪代码还容易看懂的东西。

 

在学习A*算法之前,首先回忆一下一个非常经典的单源最短路算法Dijkstra

1)维护一个表dist,储存当前求出的各点到S的距离

2)取出dist表中的最小值(显然这个过程是可以用堆优化的),并用该最小值对其他各点的dist值做松弛更新

3)重复2)过程,直到取出的最小值对应节点为T

 

让我们来看一下Dijkstra的python实现

 1 def dijkstra_search(graph, start, goal):
 2     frontier = PriorityQueue()
 3     frontier.put(start, 0)
 4     came_from = {}
 5     cost_so_far = {}
 6     came_from[start] = None
 7     cost_so_far[start] = 0
 8     
 9     while not frontier.empty():
10         current = frontier.get()
11         
12      #Early Exit!
13         if current == goal:
14             break
15         
16         for next in graph.neighbors(current):
17             new_cost = cost_so_far[current] + graph.cost(current, next)
18             if next not in cost_so_far or new_cost < cost_so_far[next]:
19                 cost_so_far[next] = new_cost
20                 priority = new_cost
21                 frontier.put(next, priority)
22                 came_from[next] = current
23     
24     return came_from, cost_so_far

 

 

在经过 启发式Breadth First Search 和 Dijkstra 的比较以后,我们发现,Dijkstra不容易被误导但像无头苍蝇一样乱撞,启发式广搜很有目的性但容易被误导撞墙

所以我们在Dijkstra的基础上引入启发式的思想,把之前以“cost_so_far[p]越小优先级越高”改进为“f[p]越小优先级越高",代码上的修改非常简单

def heuristic(a, b):
    (x1, y1) = a
    (x2, y2) = b
    return abs(x1 - x2) + abs(y1 - y2)

def a_star_search(graph, start, goal):
    frontier = PriorityQueue()
    frontier.put(start, 0)
    came_from = {}
    cost_so_far = {}
    came_from[start] = None
    cost_so_far[start] = 0
    
    while not frontier.empty():
        current = frontier.get()
        
        if current == goal:
            break
        
        for next in graph.neighbors(current):
            new_cost = cost_so_far[current] + graph.cost(current, next)
            if next not in cost_so_far or new_cost < cost_so_far[next]:
                cost_so_far[next] = new_cost

                #Notice!!Crucial difference!
                priority = new_cost + heuristic(goal, next)

                frontier.put(next, priority)
                came_from[next] = current
    
    return came_from, cost_so_far

这里给出的新的f(p)=cost_so_far(p)+heuristic(p)

其中启发式函数heuristic(p)在此处选用了曼哈顿距离作为启发函数

看起来非常有道理。

 

但是我当时对priority如此修改的正确性有怀疑,因为之前以cost_so_far划分优先级,是利用了”确定cost_so_far最小的点最短路一定已经确定“

现在的问题是,按照f(p)的优先级拓展,p点的最短路是否在拓展前已经确定?

回答这个问题的关键是意识到,"f(p)是递减的,且f(goal)的最终值就是最短路的长度”

其实我们不妨换一个想法,顺推有点想不通,尝试逆推证明其正确性

"当f(goal)都从堆里被取出来,说明其他点的历史f值或当前f值,因为f值在启发函数选用得当的情况下,总是大于实际最短路的长度的,也就是经过这些点的长度dis(goal)=f(goal)<dis(p)<f(p),这些p都没有可能产生新的使之更小的f(goal),而f(goal)就是我们最终想要的答案"

 

这让我想到卷福的一句话

After eliminating all other possibilities, the one remaining-no matter how unlikely-must be the truth.

 

posted @ 2017-02-09 21:44  K.Nick  阅读(1477)  评论(3编辑  收藏  举报
$a = (1-\sin(\theta))$