• A* 算法


    转自 http://www.raywenderlich.com/zh-hans/21503/a%E6%98%9F%E5%AF%BB%E8%B7%AF%E7%AE%97%E6%B3%95%E4%BB%8B%E7%BB%8D

    这篇blog是由iOS Tutorial Team的成员  Johann Fradj发表的,他眼下是一位全职的资深iOS开发project师。他是Hot Apps Factory的创始人,该公司开发了App Cooker

     

    Learn how the A* Pathfinding Algorithm Works!

    学习A星寻路算法是怎样工作的!

    你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢?

    假设是的话,请看这篇教程,我们会展示怎样使用A星寻路算法来实现它!

    在网上已经有非常多篇关于A星寻路算法的文章,可是大部分都是提供给已经了解基本原理的高级开发人员的。

    本篇教程将从最主要的原理讲起。我们会一步步解说A星寻路算法,幷配有非常多图解和样例。

    无论你使用的是什么编程语言或者操作平台,你会发现本篇教程非常有帮助,由于它在非编程语言的层面上解释了算法的原理。稍后,会有一篇教程,展示怎样在Cocos2D iPhone 游戏中实现A星算法。

    如今找下到达一杯咖啡因饮料和美味的零食的最短路径,開始吧!:]

    一仅仅探路猫

    让我们想象一下,有一款游戏,游戏中一仅仅猫想要找到获取骨头的路线。

    “为什么会有一仅仅猫想要骨头?!”你可能会这么想。在本游戏中, 这是一仅仅狡猾的猫,他想捡起骨头给狗,以防止被咬死!:]

    如今想像一下下图中的猫想找到到达骨头的最短路径:

    This cat just wants someone to throw him a bone!

    不幸的是,猫不能直接从它当前的位置走到骨头的位置,由于有面墙挡住了去路,并且它在游戏中不是一仅仅幽灵猫!

    游戏中的猫相同懒惰,它总是想找到最短路径,这样当他回家看望它的女朋友时不会太累:-)

    可是我们怎样编写一个算法计算出猫要选择的那条路径呢?A星算法解救了我们!

     

    简化搜索区域

     

    寻路的第一步是简化成easy控制的搜索区域。

    怎么处理要依据游戏来决定了。比如,我们能够将搜索区域划分成像素点,可是这种划分粒度对于我们这款基于方块的游戏来说太高了(不是必需)。

    作为取代,我们使用方块(一个正方形)作为寻路算法的单元。其它的形状类型也是可能的(比方三角形或者六边形),可是正方形是最简单而且最适合我们需求的。

    像那样去划分,我们的搜索区域能够简单的用一个地图大小的二维数组去表示。所以假设是25*25方块大小的地图,我们的搜索区域将会是一个有625个正方形的数组。假设我们把地图划分成像素点,搜索区域就是一个有640,000个正方形的数组了(一个方块是32*32像素)!

    如今让我们基于眼下的区域,把区域划分成多个方块来代表搜索空间(在这个简单的样例中,7*6个方块 = 42 个方块):

    Dividing the maze into a tile-based search area

    Open和Closed列表

     

    既然我们创建了一个简单的搜索区域,我们来讨论下A星算法的工作原理吧。

    除了懒惰之外,我们的猫没有好的记忆力,所以它须要两个列表:

    1. 一个记录下全部被考虑来寻找最短路径的方块(称为open 列表)
    2. 一个记录下不会再被考虑的方块(成为closed列表)

    猫首先在closed列表中加入当前位置(我们把这个開始点称为点 “A”)。然后,把全部与它当前位置相邻的可通行小方块加入到open列表中。

    下图是猫在某一位置时的情景(绿色代表open列表):

    Adding adjacent tiles from the start position to the open list

    如今猫须要推断在这些选项中,哪项才是最短路径,可是它要怎样去选择呢?

    在A星寻路算法中,通过给每个方块一个和值,该值被称为路径增量。让我们看下它的工作原理!

    路径增量

    我们将会给每一个方块一个G+H 和值:

    • G是从開始点A到当前方块的移动量。所以从開始点A到相邻小方块的移动量为1,该值会随着离開始点越来越远而增大。
    • H是从当前方块到目标点(我们把它称为点B,代表骨头!)的移动量估算值。这个常被称为探视,由于我们不确定移动量是多少 – 不过一个估算值。

    你或许会对“移动量”感兴趣。在游戏中,这个概念非常easy – 不过方块的数量。

    然而,在游戏中你能够对这个值做调整。比如:

    • 假设你同意对角线移动,你能够针对对角线移动把移动量调得大一点。
    • 假设你有不同的地形,你能够将对应的移动量调整得大一点 – 比如针对一块沼泽,水,或者猫女海报:-)

    这就是大概的意思 – 如今让我们具体分析下怎样计算出G和H值。

    关于G值

     

    G是从開始点A到达当前方块的移动量(在本游戏中是指方块的数目)。

    为了计算出G的值,我们须要从它的前继(上一个方块)获取,然后加1。所以,每一个方块的G值代表了从点A到该方块所形成路径的总移动量。

    比如,下图展示了两条到达不同骨头的路径,每一个方块都标有它的G值:

    An illustration of the G variable in the A* Pathfinding Algorithm

    关于H值

    H值是从当前方块到终点的移动量估算值(在本游戏中是指方块的数目)。

    移动量估算值离真实值越接近,终于的路径会更加精确。假设估算值停止作用,非常可能生成出来的路径不会是最短的(可是它可能是接近的)。这个题目相对复杂,所以我们不会再本教程中解说,可是我在教程的末尾提供了一个网络链接,对它做了非常好的解释。

    为了让它更简单,我们将使用“曼哈顿距离方法”(也叫“曼哈顿长”或者“城市街区距离”),它仅仅是计算出距离点B,剩下的水平和垂直的方块数量,略去了障碍物或者不同陆地类型的数量。

    比如,下图展示了使用“城市街区距离”,从不同的開始点到终点,去估算H的值(黑色字):

    An illustration of the H variable in the A* pathfinding algorithm with the Manhattan algorithm

    A星算法

    既然你知道怎样计算每一个方块的和值(我们将它称为F,等于G+H),  我们来看下A星算法的原理。

    猫会反复下面步骤来找到最短路径:

    1. 将方块加入到open列表中,该列表有最小的和值。且将这个方块称为S吧。
    2. 将S从open列表移除,然后加入S到closed列表中。
    3. 对于与S相邻的每一块可通行的方块T:
      1. 假设T在closed列表中:无论它。
      2. 假设T不在open列表中:加入它然后计算出它的和值。
      3. 假设T已经在open列表中:当我们使用当前生成的路径到达那里时,检查F 和值是否更小。假设是,更新它的和值和它的前继。

    假设你对它的工作原理还有点疑惑,不用操心 – 我们会用样例一步步介绍它的原理!:]

    猫的路径

    让我们看下我们的懒猫到达骨头的行程样例。

    在下图中,我依据下面内容,列出了公式F = G + H 中的每项值:

    • F(方块的和值):左上角
    • G(从A点到方块的移动量):左下角
    • H(从方块到B点的估算移动量): 右下角

    同一时候,箭头指示了到达对应方块的移动方向。

    最后,在每一步中,红色方块表示closed列表,绿色方块表示open列表。

    好的,我们開始吧!

    第一步

    第一步,猫会确定相对于開始位置(点A)的相邻方块,计算出他们的F和值,然后把他们加入到open列表中:

    A* Example Part 1

    你会看到每一个方块都列出了H值(有两个是6,一个是4)。我建议依据“城市街区距离”去计算方块的相关值,确保你理解了它的原理。

    同一时候注意F值(在左上角)是G(左下角)值和H(右下脚)值的和。
    第二步

    在第二步中,猫选择了F和值最小的方块,把它加入到closed列表中,然后检索它的相邻方块的相关数值。

    A* Example Part 2

    如今你将看到拥有最小增量的是F值为4的方块。猫尝试加入全部相邻的方块到open列表中(然后计算他们的和值),除了猫自身的方块不能加入以外(由于它已经被加入到了closed列表中)或者它是墙壁方块(由于它不能通行)。

    注意被加入到open列表的两个新方块,他们的G值都添加了1,由于他们如今离開始点有2个方块远了。你或许须要再计算下“城市街区距离”以确保你理解了每一个新方块的H值。


    第三步

    再次,我们选择了有最小F和值(5)的方块,继续反复之前的步骤:
    A* Example Part 3

    如今,仅仅有一个可能的方块被加入到open列表中了,由于已经有一个相邻的方块在close列表中,其它两个是墙壁方块。

    第四步

    如今我们遇到了一个有趣的情况。正如你之前看到的,有4个方块的F和值都为7 – 我们要怎么做呢?!

    有几种解决方法能够使用,可是最简单(高速)的方法是一直跟着近期被加入到open列表中的方块。如今继续沿着近期被加入的方块前进。

    A* Example Part 4

    这次有两个可通过的相邻方块了,我们还是像之前那样计算他们的和值。
    第五步

    接着我们选择了最小和值(7)的方块,继续反复之前的步骤:

    A* Example Part 5

    我们越来越接近终点了!

    第六步

    你如今训练有素了!我打赌你可以猜出下一步是以下这样子了:

    A* Example Part 6

    我们差点儿相同到终点了,可是这次你看到有两条到达骨头的最短路径提供给我们选择:

    Two shortest paths to the bone

    在我们的样例中,有两条最短路径:

    • 1-2-3-4-5-6
    • 1-2-3-4-5-7

    It doesn’t really matter which of these we choose, it comes down to the actual implementation in code.

    选择哪一条事实上没关系,如今到了真正用代码实现的时候了。

    第七步

    让我们从当中一块方块,再反复一遍步骤吧:

    A* Example Part 7

    啊哈,骨头在open列表中了!
    第八步

    如今目标方块在open列表中了,算法会把它加入到closed列表中:

    A* Example Part 8

    然后,算法要做的全部事情就是返回,计算出终于的路径!
    A* Example Part 9

    一仅仅有远见的猫

    在上面的样例中,我们看到当猫在寻找最短路径时,它常常选择更好的方块(那个在它的未来最短路径上的方块)- 好像它是一仅仅有远见的猫!

    可是假设猫是盲目的,而且总是选择第一个加入到它的列表上的方块,会发生什么事情?

    下图展示了全部在寻找过程中会被使用到的方块。你会看到猫在尝试很多其它的方块,可是它仍然找到了最短路径(不是之前的那条,而是还有一条等价的):
    What would happen if the cat wasn't so smart...

    图中的红色方块不代表最短路径,它们仅仅是代表在某个时候被选择为“S”的方块。

    我建议你看着上面的图,而且尝试过一遍步骤。这次不管你看到哪个相邻的方块,都选择“最坏”的方式去走。你会发现最后还是找到了最短路径!

    所以你能够看到尾随一个“错误的”方块是没有问题的,你仍然会在多次反复尝试后找到最短路径。

    所以在我们的实现中,我们会依照下面的算法加入方块到open列表中:

    • 相邻的方块会返回这些顺序: 上面/左边/以下/右边。
    • 当全部的方块都有同样的和值后,方块会被加入到open列表中(所以第一个被加入的方块是第一个被猫挑选的)。

    以下是从原路返回的示意图:
    The cat finding the shortest path, even after some wrong turns

    最短的路径是从终点開始,一步步返回到起点构成的(样例:在终点我们能够看到箭头指向右边,所以该方块的前继在它的左边)。

    总的来说,我们能够用以下的伪代码,合成猫的寻找过程。这是Objective-C写的,可是你能够用不论什么的语言去实现它:



  • 相关阅读:
    聊天机器人遇到的难题
    Node.js应用程序一起使用Webpack的4个简单步骤
    AngularJS页面渲染完成之后执行DOM操作
    js实现取出数组中的最大数和最小数
    npm安装angular-ui-bootstrap和bower安装
    centos7离线安装nginx
    配置SSH免密登录,总是需要输入密码
    idea win 常用快捷键
    二、centos7在线搭建ceph之Filesystem
    k8s使用cephfs来挂载mysql数据
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4021093.html
Copyright © 2020-2023  润新知