工作中需要优化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. 如何在循环中使用await?

    我正在尝试创建一个异步控制台应用程序,对集合进行一些操作.我有一个版本使用并行for循环,使用异步/等待.我预计异步/等待版本的工作类似于并行版本,但它同步执行.是什么原因呢? class Progr ...

  2. jenkins--java配置

    进入jenkins然后-->系统配置-->Global Tool Configuration

  3. day10 局部变量 全局变量 作用域前奏

    规则命名以及基本介绍 name="LHF" # 顶头写的全局都可以调用的就是全局变量,命名规则要求大写全局变量 def chang_name(): # global name # ...

  4. LOJ #6436. 「PKUSC2018」神仙的游戏(字符串+NTT)

    题面 LOJ #6436. 「PKUSC2018」神仙的游戏 题解 参考 yyb 的口中的长郡最强选手 租酥雨大佬的博客 ... 一开始以为 通配符匹配 就是类似于 BZOJ 4259: 残缺的字符串 ...

  5. Deep Learning(深度学习)学习笔记整理系列 一

    声明: 1)该Deep Learning的学习系列是整理自网上很大牛和机器学习专家所无私奉献的资料的.具体引用的资料请看参考文献.具体的版本声明也参考原文献. 2)本文仅供学术交流,非商用.所以每一部 ...

  6. 自学工业控制网络之路1.1-工业控制系统发展历程CCS DCS FCS

    返回 自学工业控制网络之路 自学工业控制网络之路1.1-工业控制系统发展历程CCS DCS FCS 工业控制系统是对诸如图像.语音信号等大数据量.高速率传输的要求,又催生了当前在商业领域风靡的以太网与 ...

  7. luogu4360 锯木厂选址 (斜率优化dp)

    设: sw[i]为1..i的w之和 sd[i]为1到i的距离 cost[i]为把第一个锯木厂建在i带来的花费 all[i,j]为把i..j所有木头运到j所需要的花费 所以$all[i,j]=cost[ ...

  8. Logstash 解析Json字符串,删除json嵌套字段

    一.场景:此文以一个简单的json字符串文件为例,描述如何用logstash解析嵌套的json,并删除其中的某些字段 我们在linux中test.json的内容如下: {"timestamp ...

  9. intent.setFlags方法中的参数值含义

     一. intent.setFlags()方法中的参数值含义: 1.FLAG_ACTIVITY_CLEAR_TOP:例如现在的栈情况为:A B C D .D此时通过intent跳转到B,如果这个int ...

  10. A1029. Median

    Given an increasing sequence S of N integers, the median is the number at the middle position. For e ...