工作中需要优化A*算法,研究了一天,最后取得了不错的效果。看网上的朋友还没有相关的研究,特此记录一下。有错误欢迎大家批评指正。如需转载请注明出处,http://www.cnblogs.com/Leonhard-/p/6842052.html,这是对作者最起码的尊重,谢谢大家。

本文结构如下:

一、A*算法优化背景介绍

二、A*算法介绍与实现简述

三、深入思考优化需求

  1.启发函数的设计思路

  2.启发函数与cost值的相对关系

  3.启发函数中对k值大小的深入思考

四、总结

一、A*算法优化背景介绍

  A*算法运用的场景很广泛,不同的运用场景有不同的A*设计思路,本文不是描述所有环境下的设计思路,而仅是记录工作中碰到的A*算法优化思路。使用的背景是在一个二维地图上,角色允许从周围的八个方向进行移动(如下图,角色在粉红色位置,周围蓝色位置为可走路线),地图中带有障碍物,求从某一个点到另外一个点的静态路径。具体的要求:1.让这个路径尽可能的短;2.让移动更加平滑,尽可能少的出现角色在选择方向上的抖动(后文有进一步介绍)。

二、A*算法介绍与实现简述

1.A*算法简单介绍

  网上已经有朋友发了短小精干却内容清晰的A*算法教程,没有研究过A*算法的朋友可以先到这个链接学习基本A*算法(如果想进一步了解A*算法的种种相关信息,可以查看原版链接或者翻译版链接)。此处则不再对重复的信息进行复制粘贴。

2.工作当中的A*算法实现思路。

  大概思路是这样的,代码用的是一个heap来存储open队列节点,用一个哈希表来存储close队列(当然也可以使用其他的方法,上述给的链接里有讲,本文就不再赘述)。

三、深入思考优化需求

1.估值函数的设计思路

其中估值函数如下:

 int estimateValue(Point gaol,Point current)
{
int dx = abs(goal.x-current.x);
int dy = abs(goal.y-current.y);
if(dx > dy)
return *dx+*dy;
else
return *dy+*dx;
}

  以dx>dy的情况为例,其中的10*dx+4*dy是什么意思呢?如下图,粉红色为需要考虑估值的点,棕色为终点,其中棕色线条的长度*10则为估值函数的估计值(放大10倍是为了去掉小数点),即10*(dx+0.4*dy)。

  

2.估值函数与cost值的相对关系

  为了说明角色往不同方向移动的cost值,我们给图的格子标上数字,方便说明,如下图。公司代码中设定,往2,4,5,7方向移动,cost值为1,往1,3,6,8位置移动,则cost值为2。细心的朋友会发现,这个地方有了错误。上述我们为了将1位小数的浮点数转换为整数,把estimate值乘了个系数10,此处也应该乘以10。即往2,4,5,7方向移动,cost值为10,往1,3,6,8位置移动,则cost值为20。根据以往的习惯,1.当目的地在位置3时,我们鼓励玩家直接从粉红点跳到3;2.当目的地点在位置5右边一个格子时,我们鼓励玩家先到5,然后再移动到5右边的格子。如果往斜方向上移动的取值为20的话,我们发现会出现一个问题,就是从粉红点直接到3的cost等于先到5再到3的cost值,这样会引起移动时角色的“抖动”(角色经常在面向3和面向5的两个方向中切换,看起来就像在抖动);如果我们把往斜方向上移动的取值为10的话,这样斜方向上和水平方向上又有了新问题,因为从粉红色格子到3再到5右边的格子的cost值等于粉红色格子先到5再到5右边格子的cost值,这样看起来比较奇怪,当地图比较大时,会更明显,当角色要从地图左下角到右下角时,角色先到地图中间的最上方,然后又往右下角进行前进,而且会增加角色的“抖动”。那么往斜方向上移动的cost值的取值范围最好是属于(10,20),这里假设我们取14,后面介绍这会出现的问题。

  

  为什么会出现角色抖动呢,分析以上我们可以知道,当estimate+cost值相等的时候,维护open队列的heap只能随机取一个,所以不能保证取出来的就一定是按我们想要的某一个方向前进的节点。那解决思路是什么呢,就是estimate+cost值尽量少的出现相等的情况,要么就是修改heap读取的方式,一目了然,前者才是个好方法。而上面的cost取值为14,经过计算我们发现,往右上方移动与往右边移动的estimate+cost的值相等,也会出现“抖动”。而此时我们将往斜上方移动的cost值取(10,14)或者(14,20),问题圆满解决了。那么往斜上方移动的cost值取(10,14)或者(14,20)有什么区别呢?看图来说明。

  当我们取(10,14)时,寻路结果如下:

  当我们取(14,20)的话,寻路结果如下:

  结合上面的两幅图,我们清楚了cost值和dy系数(后文称为k)之间的关系(只考虑dx>dy的情况,dy>dx情况类似)。当斜方向与水平方向的cost的插值大于k时,寻路会优先往x方向走,一直走到dx == dy时候开始走斜线,反之则先走斜线,后走水平线。

3.估值函数中的k值的深入思考

  上一小节我们讨论了估值函数中的k值和往斜上方走的cost值的相对关系,那么我们进一步思考,这个k值是否可以随便取呢。答案:是,但也不全是。为什么这么说呢?具体的k值对性能和速度有很大影响,如果k的选取让estimate<=从该点到目标节点的实际cost值,那么它能取到最优解,但是也因为estimate+cost的值较小的原因,heap考虑的节点数变多,让算法的速度减慢;反之,如果k的选取让estimate>从该点到目标节点的实际cost值,那么它不一定取到最优解,此时A*算法便不再是A*算法,沦为了Greedy first-search algorithm,因为estimate+cost的值较大的原因,heap考虑的节点数变少,让算法的速度加快,但是也不能保证最优解了。所以,为了取到最优解,k的值不能大于4。

四、总结

  本文介绍了二维地图上A*算法的估值函数的设计思路并讨论了它与cost值的相对关系及其影响。综上所述,二维地图上为了保证让角色走最短路径,且让角色尽量不出现“抖动”,那么估值函数设计为 G = 10*dx + 4*dy就比较好。而且考虑到项目组的游戏地图为矩形如下图(黄色),建筑物的俯视图如下图(黑色),先往水平方向走比先往斜方向走好看,所以往斜上方走的cost值取(14,20)比较好。

寻路优化(一)——二维地图上A*启发函数的设计探索的更多相关文章

  1. 寻路优化(二)——二维地图上theta*算法的设计探索

    这篇文章是基于上一篇文章的研究上进行的,使得路径更加的平滑和自然,特此记录.有错误欢迎大家批评指正.如需转载请注明出处,http://www.cnblogs.com/Leonhard-/p/68660 ...

  2. 【opengl】OpenGL中三维物体显示在二维屏幕上显示的变换过程

    转自:http://blog.sina.com.cn/s/blog_957b9fdb0100zesv.html 为了说明在三维物体到二维图象之间,需要经过什么样的变换,我们引入了相机(Camera)模 ...

  3. PHP如何判断一个数组是一维数组或者是二维数组?用什么函数?

    如题:如何判断一个数组是一维数组或者是二维数组?用什么函数? 判断数量即可 <?php if (count($array) == count($array, 1)) { echo '是一维数组' ...

  4. php背景图片上生成二维码,二维码上带logo 代码示例 (原)

    依赖库文件 phpqrcode.php (下载地址://www.jb51.net/codes/189897.html :或者在官网下载:http://phpqrcode.sourceforge.net ...

  5. SuperMap 二维地图和三维场景弹窗窗口大小控制

    注:此处所说的弹窗窗口,主要指的是那些弹窗窗口中嵌入iframe,包含信息页面的窗口大小控制. 1.首先来了解下 SuperMap 示例中的处理方案 二维的处理方式 //初始化Anchored类 po ...

  6. LGV 引理——二维DAG上 n 点对不相交路径方案数

    文章目录 引入 简介 定义 引理 证明 例题 释疑 扩展 引入 有这样一个问题: 甲和乙在一张网格图上,初始位置 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_ ...

  7. 关于微信扫描二维码下载apk文件的细节设计

    微信使用的人数越来越多,渐渐的用户形成了一种习惯,扫描二维码的时候,也会打开微信去扫描,但是微信不支持第三方的链接下载,有些厂商已经发现了这一特点,所以在使用二维码下载自家的app时,会做一个提示,引 ...

  8. Qt信号槽机制的实现(面试的感悟,猜测每一个类保存的一个信号和槽的二维表,实际使用函数指针 元对象 还有类型安全的检查设定等等)

    因为面试时问了我这道题,导致我想去了解信号槽到底是如何实现的,于是贴着顺序看了下源码,大致了解了整个框架.网上关于信号槽的文章也很多,但是大部分都是将如何应用的,这里我就写一下我所理解的如何实现吧, ...

  9. 牛客训练六:海啸(二维树状数组+vector函数的使用)

    题目链接:传送门 思路: 二维树状数组, vector(first,last)函数中assign函数相当于将first中的函数清空,然后将last中的值赋值给first. 参考文章:传送门 #incl ...

随机推荐

  1. 英国电信反悔华为是唯一真正的5G供应商

    导读 英国电信反悔华为是唯一真正的5G供应商 英国电信的一位发言人表示,该公司目前正「从我们自 2006 年以来实施的网络架构原则中,从我们的 3G 和 4G 网络核心提取华为设备」. 英国电信已经不 ...

  2. MT【231】棋子方法数

    设有5枚无区别的棋子放在如图$5*5$的棋盘的小方格中,放棋子的规则是每行每列放且仅放一个棋子,同时,不允许放在黑方格内,则共有______ 方法. 答案:5的错排数44.第一行的数不放第二列(相当于 ...

  3. MT【222】几道自招面试真题

    1.(2015东南大学)2分钟找杯子,4分钟找茶叶,7分钟烧水,爸爸花13分钟给家访的老师泡茶,请问你怎么看? 2.(2014复旦大学)共有5顶帽子,三个黑的两个白的,三个人排成一排,并让这三个人每个 ...

  4. 【ZOJ2277】The Gate to Freedom

    BUPT2017 wintertraining(16) #4 E ZOJ - 2277 题意 输出\(n^n\)的首位的数字. 题解 用科学计数法表示\(n^n=k\cdot 10^b\),那么\(n ...

  5. Leetcode 50.Pow(x,n) By Python

    实现 pow(x, n) ,即计算 x 的 n 次幂函数. 示例 1: 输入: 2.00000, 10 输出: 1024.00000 示例 2: 输入: 2.10000, 3 输出: 9.26100 ...

  6. 【原创】python多线程测试接口性能

    除了使用性能测试工具进行性能测试,我们也可以直接用python多线程进行性能测试. 下面,使用这几个模块,对一个查询接口做性能测试: requests:发送http请求 json:返回的字符串转换成j ...

  7. poj1038 Bugs Integrated,Inc. (状压dp)

    题意:N*M的矩阵,矩阵中有一些坏格子,要在好格子里铺2*3或3*2的地砖,问最多能铺多少个. 我的方法好像和网上流传的方法不太一样...不管了.... 由数据范围很容易想到状压dp 我们设某个状态的 ...

  8. 跟我一起使用electron搭建一个文件浏览器应用吧(三)

    第二篇博客中我们可以看到我们构建的桌面应用会显示我们的文件及文件夹. In the second blog, we can see that the desktop application we bu ...

  9. mapping生成sam文件时出现[mem_sam_pe] paired reads have different names错误

    用以下命令修复: bbrename.sh in1=read1.fq in2=read2.fq out1=renamed1.fq out2=renamed2.fq bbrename.sh 下载地址网上自 ...

  10. Golang面向对象编程-struct(结构体)

    Golang面向对象编程-struct(结构体) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是面向对象编程 面向对象编程(Object Oriented Program ...