记录一个A星算法介绍的网站,包括A星算法、广度优先搜索、Dijkstra算法介绍、试验结果和动画演示。
参考链接:
http://theory.stanford.edu/~amitp/GameProgramming/
https://www.redblobgames.com/pathfinding/a-star/introduction.html#graphs
在游戏中,我们经常希望找到从一个位置到另一个位置的路径。我们不仅要找到最短的距离;我们还想考虑旅行时间。移动 blob(起点)和交叉点以查看最短路径。
要找到此路径,我们可以使用图形搜索算法,该算法在地图表示为图形时起作用。A* 是图形搜索的热门选择。广度优先搜索是最简单的图形搜索算法,所以让我们从那里开始,我们将逐步达到 A*。
表示地图
研究算法时要做的第一件事是理解数据。输入是什么?输出是什么?
输入:图搜索算法,包括 A*,将“图”作为输入。图形是一组位置(“节点”)和它们之间的连接(“边”)。这是我给 A* 的图表:
A* 看不到其他任何内容。它只能看到图形。它不知道有什么东西是在室内还是室外,或者它是一个房间还是一个门口,或者一个区域有多大。它只看到图表!它不知道这张地图和这另一个.
输出:A* 找到的路径是由图形节点和边组成.边缘是抽象的数学概念。A* 会告诉您从一个位置移动到另一个位置,但它不会告诉您如何移动。请记住,它对房间或门一无所知;它看到的只是图表。您必须确定 A* 返回的图形边是否意味着从一个图块移动到另一个图块或沿直线行走、打开门或游泳或沿着弯曲路径奔跑。
权衡:对于任何给定的游戏地图,有许多不同的方法可以制作路径查找图以提供给 A*。上图将大多数门口变成了节点;如果我们制作门口进入边缘?如果我们使用寻路网格?
路径查找图不必与游戏地图使用的图相同。网格游戏地图可以使用非网格寻路图,反之亦然。A* 以最少的图形节点运行最快;网格通常更容易使用,但会产生大量节点。本页介绍 A* 算法,但不包括图形设计;查看我的其他页面有关图表的更多信息。对于页面其余部分的解释,我将使用网格,因为它更容易可视化概念。
广度优先搜索
广度优先搜索在所有方向上平等地探索。这是一种非常有用的算法,不仅适用于常规路径查找,还适用于程序地图生成、流场寻路、距离图和其他类型的地图分析。
python代码实现
frontier = Queue()
frontier.put(start )
reached = set()
reached.add(start)
while not frontier.empty():
current = frontier.get()
for next in graph.neighbors(current):
if next not in reached:
frontier.put(next)
reached.add(next)
Dijkstra
Dijkstra的算法(也称为统一成本搜索)让我们优先考虑要探索的路径。它不是平等地探索所有可能的路径,而是倾向于低成本路径。我们可以分配更低的成本来鼓励在道路上移动,分配更高的成本来避开敌人,等等。当移动成本发生变化时,我们使用它而不是广度优先搜索。
在 Dijkstra 算法中,我们使用从起点的实际距离进行优先级队列排序。相反,在贪婪最佳优先搜索中,我们将使用到目标的估计距离进行优先级队列排序。将首先探索离目标最近的位置。该代码使用来自 Dijkstra 算法的优先级队列,但没有:cost_so_far
frontier = PriorityQueue()
frontier.put(start, 0)
came_from = dict()
came_from[start] = None
while 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
A*
A* 是对 Dijkstra 算法的修改,针对单个目的地进行了优化。Dijkstra的算法可以找到到所有位置的路径;A* 查找指向一个位置或多个位置中最近的路径。它优先考虑似乎更接近目标的路径。
Dijkstra的算法可以很好地找到最短的路径,但它浪费了时间去探索没有希望的方向。贪婪的最佳优先搜索探索有希望的方向,但它可能找不到最短的路径。A* 算法同时使用从起点开始的实际距离和到目标的估计距离。
代码与Dijkstra的算法非常相似:
frontier = PriorityQueue()
frontier.put(start, 0)
came_from = dict()
cost_so_far = dict()
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
priority = new_cost + heuristic(goal, next)
frontier.put(next, priority)
came_from[next] = current