【算法】A*算法与启发函数
来源:互联网 发布:vb mscomctl.ocx 编辑:程序博客网 时间:2024/06/05 06:03
写这篇是因为看到国外一篇讲的巨好的关于A*算法的文章——Introduction to A*。图文并茂,而且讲了一些A*算法的来龙去脉,有些观点也醍醐灌顶啊,所以赶紧来总结一下。
A*算法为什么叫这个名
这个从wiki上看来的,一开始是57年提出的Dijkstra算法,然后64年Nils Nilsson提出了A1算法,是一个启发式搜索算法,而后又被改进成为A2算法,直到68年,被Peter E. Hart改进成为A*算法,为什么叫A*呢,因为原作者借鉴了统计学方面的一个*上标,在统计学中,一个变量加上 * 表示这个变量的最优解,所以作者认为他们是最优解,是前人(A算法)的集大成者,所以叫A*。
BFS和Dijkstra算法
要讲A*算法就必须去讲Dijkstra算法,因为Dijkstra算法可以看成A*算法的特例。而BFS又可以看成Dijkstra的特例,
对于BFS算法,python代码如下:
frontier = Queue()frontier.put(start)came_from = {}came_from[start] = Nonewhile not frontier.empty(): current = frontier.get() if current == goal: break for next in graph.neighbors(current): if next not in came_from: frontier.put(next) came_from[next] = current
对于Dijkstra算法,python代码如下:
frontier = PriorityQueue() #这个地方用优先队列了frontier.put(start, 0)came_from = {}cost_so_far = {}came_from[start] = Nonecost_so_far[start] = 0while 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 priority = new_cost frontier.put(next, priority) came_from[next] = current
Dijkstra算法是一个贪心算法/也可以看成动规。其是每次都找离起点最近的点
Dijkstra算法保证能得到最优解,但是扫描的区域会比较的大。
贪心搜索算法(Greedy Best First Search)
Dijkstra算法搜索的空间太大了,能不能利用网格图的一些特点来简化搜索呢?想到了贪心算法。
贪心搜索算法,是每次找离终点距离最近的点。
另外如何定义这个距离呢,一般网格图是用曼哈顿距离来定义的。即两个点之间坐标差的绝对值之和。
def heuristic(a, b): # Manhattan distance on a square grid return abs(a.x - b.x) + abs(a.y - b.y)frontier = PriorityQueue()frontier.put(start, 0)came_from = {}came_from[start] = Nonewhile not frontier.empty(): current = frontier.get() if current == goal: break for next in graph.neighbors(current): if next not in came_from: priority = heuristic(goal, next) frontier.put(next, priority) came_from[next] = current
启发函数的性质
那么能不能综合上面Dijkstra算法得到最优解和贪心算法速度快的特点,有更好的办法呢?
【注意一下这个地方,Dijkstra算法是适用于任何图算法找最短距离均可的,但是用到启发式算法的话,大部分情况下会是一个方格图,因为只有方格图才能比较好的估算从当前点到终点的距离】
那么我们可以先定义一个估算函数
其中
所以上面两个合起来就是其走当前点到终点的总代价函数
而
- 先考虑极端情况, 如果
h(n)=0 的情况下,只有g(n) 起作用,那么A*算法就是Dijkstra算法。- 如果
h(n) 始终小于等于实际n 点到终点的距离,那么必然能够保证A*算法找的解就是最优解。而且h(n) 越小,则A*扩展的节点也就越多,A*算法运行的也就越慢。- 如果
h(n) 始终都等于实际n 点到终点的距离,那么A*算法只会严格走从起点到终点的最短路径。虽然这种情况一般不可能发生,当然一些特殊情况下会发生【比如没有障碍物的情况】。- 如果
h(n) 有时候大于实际n 点到终点的距离,那么不能保证A*算法能够找到最短路径- 另外一种极端情况,就是如果只有
h(n) 发挥作用,则A*算法就相当于贪心算法。
A*算法
一般选择曼哈顿距离的A*算法被称为simply A算法,不过因为这种方法被用的最广泛,所以一般不加以区分了。实际上还有很多别的变种的。
A*算法的python代码,可以看到跟Dijkstra算法非常像啦,只不过加到队列里的优先级是
frontier = PriorityQueue()frontier.put(start, 0)came_from = {}cost_so_far = {}came_from[start] = Nonecost_so_far[start] = 0while 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 priority = new_cost + heuristic(goal, next) frontier.put(next, priority) came_from[next] = current
A*算法跑的时间要小于Dijkstra,同时能够保证找的路径是最短路径。
附录
关于这些路径搜索算法的程序详见:http://www.redblobgames.com/pathfinding/a-star/implementation.html
上面的PriorityQueue()详见下面的实现:
import heapqclass PriorityQueue: def __init__(self): self.elements = [] def empty(self): return len(self.elements) == 0 def put(self, item, priority): heapq.heappush(self.elements, (priority, item)) def get(self): return heapq.heappop(self.elements)[1]
参考资料
以下两个资料写的都非常的好,第一个
- Introduction to A*介绍的那个A*算法,里面各种动画非常丰富。
- Pathfinding这个资料非常的长,是1资料的加长版,里面详细讲了各种路径搜索算法,和改进。
- 【算法】A*算法与启发函数
- A*算法中启发函数的使用
- 根据启发函数(A*算法)求解八数码问题
- 【五子棋AI】启发算法——PV与杀手启发
- 启发算法,元启发算法的优势?
- A*算法 与 IDA*算法
- A算法与A*算法区别
- A*算法中的估价函数
- 【五子棋AI】启发算法——迭代加深与内部迭代加深
- 启发函数
- A*算法理论与实践
- A*算法理论与实践
- A*算法理论与实践
- A*算法理论与实践
- A*算法与matlab实现
- 仿函数与if算法
- [数据结构与算法]函数依赖
- connect函数与karn算法
- ZOJ2343 Robbers(贪心)
- ListView
- 颓废
- Unity 3D 正交(Orthographic)摄像机尺寸学习笔记
- Android性能优化
- 【算法】A*算法与启发函数
- UNIX.3rd.Edition.The.Textbook. unix系统基础
- 《Java编程思想》学习记录(二)
- HashMap里的equals实现
- Linux锐捷登录
- POJ 1562 Oil Deposits
- 持久化
- BOSS的AI(自动寻路和自动攻击)
- 最长公共子序列问题