(笔记) A*启蒙
来源:互联网 发布:男士保湿 知乎 编辑:程序博客网 时间:2024/05/04 05:01
原文很精彩!
地图表示
当理解算法的时候,第一件事是理解数据,什么是输入?什么是输出?
输入:图形搜索算法,包括A*,把“图”作为输入.一个图是地点(节点)的集合和地点之间的连线(边)
A*不知道任何其他信息。它只知道图。它不知道一个节点是房间还是门口或者一个区域多大。
输出: A*算法发现的路径由节点和边组成。边是抽象的数学概念。A*告诉你从一个地点移动到另一个地点,但不会告诉你怎么移动过去的。
权衡:对于任意给定的游戏地图,有很多种方法制作一个路径发现的图给A*使用。我们可以把门作为节点,也可以把门做为边,也可以使用路径发现网格。
用于路径发现的地图不需要和你游戏地图一模一样。一个网格游戏地图可以使用非网格路径搜索图,反之亦然。图节点越少A*算法越快。网格更容易操作但是会导致很多节点。本文讲解A*算法而不是地图设计;访问我的另一篇文章获取更多图的信息。为了方便解释,本文接下来的内容我将使用网格讲解。
算法
广度优先搜索向每个方向均等地搜索。这是一个非常好用的算法,不仅仅用于常规的路径搜索,还可以用于地图生成,流场寻路,距离地图,和其他地图分析。
迪杰斯特拉算法(也被称作成本一致搜索法)让我们优先探索一部分路径,它青睐低成本的路径,而不是均等地探索所有可能的路径。我们可以给马路赋予低成本来促进走在马路上,给森林赋予高成本来避免森林,给敌人赋予高成本来避免靠近它们。当移动的成本不同时,我们使用这个算法而不是广度优先搜索算法。
A*算法是迪杰斯特拉算法的一个改版,用以优化单个目的地的寻路。迪杰斯特拉算法可以发现到所有地点的路径;A*算法只能发现到一个地点的路径。它把更高的优先权赋予给那先看起来和目标更近的路径。
广度优先搜索
这些所有算法的关键想法是我们能够跟踪被称作边界的扩展圆环。
frontier = Queue()frontier.put(start)visited = {}visited[start] = Truewhile not frontier.empty(): current = frontier.get() for next in graph.neighbors(current): if next not in visited: frontier.put(next) visited[next] = True
这个循环是本页搜索算法的本质,包括A*。但它只告诉我们怎么访问地图上的每个元素,实际上并没有构造路径。所以对于每个节点,我们需要记录它是从哪个节点来的,我们将visited数组重命名为came_from:
frontier = Queue()frontier.put(start)came_from = {}came_from[start] = Nonewhile not frontier.empty(): current = frontier.get() for next in graph.neighbors(current): if next not in came_from: frontier.put(next) came_from[next] = current
重构路径的代码很简单
current = goal path = [current]while current != start: current = came_from[current] path.append(current)path.append(start) # optionalpath.reverse() # optional
提早退出
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
移动成本
在一些路径搜索的场景中,不同的移动有不同的成本。比如在文明这款游戏中,穿过平地和沙漠的成本是1而穿过森林和山可能是5.
让我们比较一下移动的次数和移动的距离
对于迪杰特斯拉算法,我们要记录移动的成本,让我们添加一个变量,cost_so_far,来记录从起始位置开始的总体移动成本。当我们从frontier中挑选location的时候,我们希望考虑移动成本的因素。为此,我们将queue变为priorityqueue。而且我们多次访问一个位置,每次访问的路径都有着不同的成本。所以,除了在location没被访问的情况下,我们需要把location记录到frontier中,如果到location的新路径的成本比之前的低,我们也需要。
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
启发式搜索
广度有限搜索和迪杰斯特拉算法的边界(frontier)朝着各个方向扩展。如果你想寻找到多个地点的路径,这是合理的。但通常情况下我们想找一个地点。我们需要让frontier朝着目标的方向扩展得更多一点。首先,我们定义一个会告诉我们和目标之间距离的启发式函数
def heuristic(a, b): #正方形网格上的哈夫曼距离 return abs(a.x - b.x) + abs(a.y - b.y)
在迪杰斯特拉算法中,我们使用从起始点开始的距离做为priority queue的排序标准。但这一次,在贪心搜索中,我们使用到目标的估计距离做为排序标准。和目标越近的地点越先被探索。代码使用来自广度优先搜索的优先队列,而不是来自迪杰斯特拉算法中的cost_so_far
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
贪心算法找到的路径不一定是最短的。所以这个算法在没有很多障碍物的时候跑得很快..我们可以解决这个问题吗?当然。
A*算法
迪杰斯特拉算法可以找到最短路径,但它在不太可能的方向上浪费了很多时间,贪心算法在可能的路径上寻找,但它找到的路径可能不是最短的。A*算法同时使用了从起始点开始的实际距离,以及到目标的估计距离。
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*就会找到最佳路径。
更多
你准备好实现它们了吗?如果你想自己实现它们,我有一个搭配的教程,一步步教你怎么实现图,队列和路径搜索算法。
那么你在游戏地图中应该使用哪个算法?
- 如果你想寻找到所有地点的路径,使用广度有限搜索或者迪杰斯特拉算法。
- 如果你只想找到一个地点,使用贪心算法或者A*,当然大多数情况下A*更棒。如果你尝试使用贪心算法,你可以考虑一下使用“inadmissible”heuristic A*算法。
广度优先搜索和迪杰斯特拉算法保证找到最短路径。贪心算法不保证。A*算法在启发式的估计值不大于真实值的时候保证找到最短路径。随着启发式的估计值变得更小,A*算法就会变为迪杰斯特拉算法。当启发式的估计值变大,A*算法就会变成贪心算法。
如果想提高性能,最佳的途径就是删去图中没用的节点。如果使用网格,可以看这里。降低图的大小有利于帮助图搜索算法。然后,使用尽可能简单的算法。
- (笔记) A*启蒙
- Linux基础笔记--我的linux启蒙篇
- LC1视觉启蒙班第一讲(2)笔记
- 4LC视觉启蒙班第四讲笔记
- 启蒙与非启蒙
- 算法启蒙
- 创业启蒙
- 算法启蒙
- 性启蒙
- 启蒙认知
- JAVA启蒙
- 学长启蒙
- SP业务新人启蒙
- 网络桥牌启蒙教程
- 古典国学启蒙
- 声律启蒙
- 实习启蒙(1)
- 实习启蒙(2)
- python爬虫之爬取百度音乐
- Linux中的粘滞位
- hdu 1863 prim初步
- Spring官方文档翻译(第九章)
- 精益创业
- (笔记) A*启蒙
- 用gem5img.py卸载目录时Permission denied: '/run/user/112/gvfs'
- Android手机拍照识别名片SDK
- Android进阶之路
- 如何解决chrome flash 过期
- 找石油
- 构造函数与析构函数
- 数字段计数
- TCP流量控制与拥塞控制