A*算法(二)启发式算法

  • 1. 启发式函数的使用
  • 2. 权衡代价函数
  • 3. 衡量单位
  • 4. 精确的启发式函数
    • 4.1 预计算的精确启发式函数
    • 4.2 线性精确启发式算法
  • 5. 网格地图中的启发式算法
    • 5.1 曼哈顿距离
    • 5.2 对角线距离
    • 5.3 欧几里得距离
    • 5.4 平方后的欧几里得距离
    • 5.5 Breaking ties Breaking ties
    • 5.6 区域搜索

1. 启发式函数的使用

启发式函数h(n)h(n)h(n)告诉A*从任意结点n到目标点的最小代价评估值
因此,选择一个好的启发式函数是重要的

情况 函数 结果
h(n)h(n)h(n) = 0 A* 演变成 Dijkstra算法 保证能找到最短路径
h(n)h(n)h(n) ≤ 实际代价 h(n)h(n)h(n)越小,A*扩展的结点越多,运行就得越慢 保证能找到一条最短路径,但运算更快了
h(n)h(n)h(n) = 实际代价 仅仅寻找最佳路径而不扩展别的任何结点 保证能找到一条最短路径,并且运算非常快
h(n)h(n)h(n) > 实际代价 寻找最佳路径且扩展别的任何结点 不能保证找到一条最短路径,但运算更快了
h(n)h(n)h(n)>>g(n)g(n)g(n) A*演变成BFS算法 不能保证找到一条最短路径,但运算非常快

所以得到一个很有趣的情况,那就是可以决定想要从A* 中获得什么
理想情况下,想最快地得到最短路径
如果目标太低,仍会得到最短路径,不过速度变慢了
如果目标太高,那就放弃了最短路径,但A* 运行得更快

A*的这个特性非常有用,例如
在某些情况下,希望得到一条好的路径(“good” path)而不是一条完美的路径(“perfect” path)
为了权衡g(n)g(n)g(n)和h(n)h(n)h(n),可以修改任意一个的权重

在学术上,如果启发式函数值是对实际代价的低估,A* 算法被称为 简单的A算法(simply A
然而,继续称之为A*,因为在实现上是一样的,并且在编程领域并不区别A和A*


2. 权衡代价函数

A* 改变它自己行为的能力基于启发式代价函数
速度精确度之间取得折衷将会让搜索运行得更快

在搜索中,并不真正需要得到最好的路径,仅需要近似的就足够了
而需要什么则取决于路径搜索中发生着什么,或者运行路径搜索的机器有多快

假设路径搜索的地图中有两种地形:平原和山地
有可能有一条沿着平原到山地的路径
在平原中的移动代价是1,而在山地则是3
把两个邻接点之间的评估距离设为1.5可以加速A* 的搜索过程
然后A* 会将3和1.5比较,这并不比把3和1比较差

速度和精确度之间的选择前不是静态的
可以基于CPU的速度、用于路径搜索的时间片数、地图上物体的数量、物体的重要性、组(group)的大小、难度或者其他任何因素来进行动态的选择

取得动态的折衷的一个方法是:
建立一个启发式函数用于假定通过一个网格空间的最小代价是1
然后建立一个代价函数(cost function)用于测量(scales): g′(n)=1+alpha∗(g(n)–1)g'(n) = 1 + alpha * ( g(n) – 1 )g′(n)=1+alpha∗(g(n)–1)
改变最短路径搜索算法的代价

如果alpha是0,则改进后的代价函数的值总是1,地形代价被完全忽略,简单地判断一个网格可否通过
如果alpha是1,则最初的最短路径搜索算法代价函数将起作用,然后得到了A*的所有优点

也可以考虑对启发式函数的返回值做选择:绝对最小代价或者期望最小代价
例如,如果地图大部分地形是代价为2的草地,其它一些地方是代价为1的道路
那么可以考虑让启发式函数不考虑道路,而只返回2*距离

当然,速度和精确度之间的选择并不是全局的


3. 衡量单位

A* 计算f(n)=g(n)+h(n)f(n) = g(n) + h(n)f(n)=g(n)+h(n)
为了对这两个值进行相加,这两个值必须使用相同的衡量单位
如果g(n)g(n)g(n)用小时来衡量而h(n)h(n)h(n)用米来衡量,那么A* 将会认为ggg或者hhh太大或者太小
因而将不能得到正确的路径,同时A* 算法将运行得更慢


4. 精确的启发式函数

如果启发式函数精确地等于实际最佳路径(optimal path
此时A*扩展的结点将非常少

A* 算法内部发生的事情是:
在每一结点它都计算 f(n)=g(n)+h(n)f(n) = g(n) + h(n)f(n)=g(n)+h(n)

当h(n)h(n)h(n)精确地和g(n)g(n)g(n)匹配时,f(n)f(n)f(n)的值在沿着该路径时将不会改变
不在最短路径上,所有结点的f(n)f(n)f(n)值均大于最短路径上的f(n)f(n)f(n)值
如果已经有较低f(n)f(n)f(n)值的结点,A* 将不考虑f(n)f(n)f(n)值较高的结点
因此它肯定不会偏离最短路径


4.1 预计算的精确启发式函数

构造精确启发函数的一种方法:
预先计算任意一对结点之间最短路径的长度

然后添加一个启发函数h′h'h′用于评估从任意位置n到达邻近导航点(waypoints)的代价
最终的启发式函数:h(n)=h′(n,w1)+distance(w1,w2)+h′(w2,goal)h(n) = h'(n, w1) + distance(w1, w2) + h'(w2, goal)h(n)=h′(n,w1)+distance(w1,w2)+h′(w2,goal)

或者如果希望一个更好但更昂贵的启发式函数
则分别用靠近结点和目标的 所有的w1,w2对 对上式进行求值


4.2 线性精确启发式算法

在特殊情况下,可以不通过预计算而让启发式函数很精确
如果有一个不存在障碍物和平缓地形
那么从初始点到目标的最短路径应该是一条直线

如果正使用简单的启发式函数(不知道地图上的障碍物)
则它应该和精确的启发式函数相符合
如果不是这样,则会遇到衡量单位的问题,或者所选择的启发函数类型的问题


5. 网格地图中的启发式算法

在网格地图中,有一些众所周知的启发式函数


5.1 曼哈顿距离

标准的启发式函数是曼哈顿距离(Manhattan distance
考虑代价函数并找到从一个位置移动到邻近位置的 最小代价D
曼哈顿距离——两点在南北方向上的距离加上在东西方向上的距离
即 D(I,J)= |XI - XJ| + |YI - YJ|

因此,启发式函数应该是曼哈顿距离的D倍:

H(n) = D * (abs ( n.x – goal.x ) + abs ( n.y – goal.y ) )


5.2 对角线距离

如果在地图中允许对角运动,那么需要一个不同的启发函数
(4 east, 4 north)的曼哈顿距离将变成8* D,然而可以简单地移动(4 northeast)代替
所以启发函数应该是4* D

假设直线和对角线的代价都是D:

h(n) = D * max(abs(n.x - goal.x), abs(n.y - goal.y))

如果对角线运动的代价不是D,但类似于D2 = sqrt(2) * D
则上面的启发函数不准确,需要一些更准确的东西:

D2 = sqrt(2) * Dh_diagonal(n) = min(abs(n.x - goal.x), abs(n.y - goal.y))h_straight(n) = (abs(n.x - goal.x) + abs(n.y - goal.y))h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n)))

h_diagonal(n):沿着斜线可以移动的步数
h_straight(n):曼哈顿距离
然后合并这两项,让所有的斜线步都乘以 D2
剩下的所有直线步(注意这里是曼哈顿距离的步数减去2倍的斜线步数)都乘以D
因为一个斜线用直折线走就得2步


5.3 欧几里得距离

如果单位可以沿着任意角度移动(而不是网格方向),那么也许应该使用直线距离:

h(n) = D * sqrt(pow(n.x - goal.x, 2) + pow(n.y - goal.y, 2))

然而,如果是这样的话,直接使用A* 时将会遇到麻烦
因为代价函数g(n)g(n)g(n)不会匹配启发函数h(n)h(n)h(n)
因为欧几里得距离比曼哈顿距离和对角线距离都短
仍可以得到最短路径,不过A*将运行得更久一些


5.4 平方后的欧几里得距离

一些A*的网页提到通过使用距离的平方而避免欧几里得距离中昂贵的平方根运算:

h(n) = D * (pow(n.x - goal.x, 2) + pow(n.y - goal.y, 2))

千万不要这样做!这明显地导致衡量单位的问题
当A* 计算f(n)=g(n)+h(n)f(n) = g(n) + h(n)f(n)=g(n)+h(n)
h(n)h(n)h(n)将比g(n)g(n)g(n)的代价大很多,并且会因为启发式函数评估值过高而停止
对于更长的距离,A*退化成BFS


5.5 Breaking ties Breaking ties

导致低性能的一个原因来自于启发函数的结点处理

当某些路径具有相同的f(n)f(n)f(n) 值的时候,它们都会被搜索,尽管只需要搜索其中的一条:

为了解决这个问题,可以为启发函数添加一个附加值
附加值对于结点必须是确定性的(也就是说,不能是随机的数),而且它必须让f(n)f(n)f(n)值体现区别

一种,对f(n)f(n)f(n)值排序,让f(n)f(n)f(n)值不同意味着只有一个等价(equivalent)的f(n)f(n)f(n)值会被检测

另一种添加附加值的方式是 稍微改变h(n)h(n)h(n)的衡量单位
如果减少h(n)h(n)h(n)的权重,那么当朝着目标移动的时候f(n)f(n)f(n)将逐渐增加
很不幸,这意味着A*倾向于扩展到靠近初始点的结点,而不是靠近目标的结点

可以增加衡量单位 p(甚至是0.1%),A* 就会倾向于扩展到靠近目标的结点

heuristic *= (1.0 + p)

选择因子p使得p < 移动一步(step)的最小代价的最长路径长度
假设不希望路径超过1000步,可以使 p = 1 / 1000
添加这个附加值的结果是,A*比以前搜索的结点更少了


当存在障碍物时,当然仍要在它们周围寻找路径
但要意识到,当绕过障碍物以后,A*搜索的区域非常少


一个更直截了当的方法是把h(n)h(n)h(n)传递到比较函数(comparison function
当f(n)f(n)f(n)值相等时,比较函数检查h(n)h(n)h(n),然后添加附加值

一个不同的添加附加值的方法是,倾向于从初始点到目标点的连线(直线):

dx1 = current.x - goal.x
dy1 = current.y - goal.ydx2 = start.x - goal.x
dy2 = start.y - goal.ycross = abs(dx1 * dy2 - dx2 * dy1)heuristic += cross*0.001

这段代码计算初始-目标向量(start to goal vector)和当前-目标向量(current point to goal vector)的向量叉积(vector cross-product)
结果是,这段代码选择的路径稍微倾向于从初始点到目标点的直线
当没有障碍物时,A*不仅搜索很少的区域,而且它找到的路径看起来非常棒

然而,因为这种附加值倾向于从初始点到目标点的直线路径,当出现障碍物时将会出现奇怪的结果
注意这条路径仍是最佳的,只是看起来很奇怪

然而,另一种添加附加值的方法是,小心地构造A*优先队列
使新插入的具有特殊f(n)f(n)f(n)值的结点总是比那些以前插入的具有相同f(n)f(n)f(n)值的旧结点要好一些


5.6 区域搜索

如果想搜索邻近目标的任意不确定结点,而不是某个特定的结点
应该建立一个启发函数h′(x)h'(x)h′(x),使得 h′(x)h'(x)h′(x)为 h1(x), h2(x), h3(x)…的最小值
而这些h1, h2, h3是邻近结点的启发函数
然而,一种更快的方法是让A*仅搜索目标区域的中心
一旦从开放且未考察的区域OPEN集合中取得任意一个邻近目标的结点
就可以停止搜索并建立一条路径了


参考:

《 Amit’s A star Page 》


相关推荐:

A* 算法(一)算法导言


谢谢!

A*算法(二)启发式算法相关推荐

  1. 使用pytorch从零开始实现YOLO-V3目标检测算法 (二)

    原文:https://blog.csdn.net/u011520516/article/details/80212960 博客翻译 这是从零开始实现YOLO v3检测器的教程的第2部分.在上一节中,我 ...

  2. 从零开始学数据结构和算法(二)线性表的链式存储结构

    链表 链式存储结构 定义 线性表的链式存储结构的特点是用一组任意的存储单元的存储线性表的数据元素,这组存储单元是可以连续的,也可以是不连续的. 种类 结构图 单链表 应用:MessageQueue 插 ...

  3. Unicode双向算法详解(bidi算法)(二)

    作者:黄邦勇帅(原名:黄勇)2019-10-17 Unicode双向算法详解(bidi算法)(二) 本文为原创文章,转载请注明出处,或注明转载自"黄邦勇帅(原名:黄勇) 本文是对<C+ ...

  4. 票据ticket实现方式java代码_Java代码实践12306售票算法(二)

    周五闲来无事,基于上一篇关于浅析12306售票算法(java版)理论,进行了java编码实践供各位读者参考(以下为相关代码的简单描述) 1.订票工具类 1.1初始化一列车厢的票据信息 /** * 生成 ...

  5. matlab算法(二维傅立叶级数变换)

    说明 Y = fft2(X) 使用快速傅里叶变换算法返回矩阵的二维傅里叶变换,这等同于计算 fft(fft(X).').'.如果 X 是一个多维数组,fft2 将采用高于 2 的每个维度的二维变换.输 ...

  6. 数据结构和算法之五:排序算法二

    数据结构基础之排序算法二 学习算法,排序算法当然是不能少的,这次我们来学习一下基础的选择排序,冒泡排序,以及大名鼎鼎的快速排序. 选择排序 选择排序,非常好理解,就是找最小的数放到第一位,然后从第二个 ...

  7. 售票java代码_Java代码实践12306售票算法(二)

    周五闲来无事,基于上一篇关于浅析12306售票算法(java版)理论,进行了java编码实践供各位读者参考(以下为相关代码的简单描述) 1.订票工具类 1.1初始化一列车厢的票据信息 /** * 生成 ...

  8. java按顺序售票方法_java_Java代码实践12306售票算法(二),周五闲来无事,基于上一篇关 - phpStudy...

    Java代码实践12306售票算法(二) 周五闲来无事,基于上一篇关于浅析12306售票算法(java版)理论,进行了java编码实践供各位读者参考(以下为相关代码的简单描述) 1.订票工具类 1.1 ...

  9. 数据结构与算法:二叉搜索树

    ✨数据结构与算法:二叉搜索树

  10. 算法设计与分析——十大经典排序算法二(6--10)

    一个不知名大学生,江湖人称菜狗 original author: jacky Li Email : 3435673055@qq.com  Time of completion:2023.3.1 Las ...

最新文章

  1. mysql 单实例部署_Mysql 数据库单机多实例部署手记
  2. 【超详细】一文学会链表解题
  3. 用于面包板的双列直插需要多宽?
  4. 机器学习理论《统计学习方法》学习笔记:第九章 EM算法及其推广
  5. BZOJ 2768 [JLOI2010]冠军调查
  6. 钱荒下银行理财收益率角逐:邮储银行垫底
  7. html中dl标签和ul标签,html中dl,dt,dd,ul,li,ol标签区别和使用
  8. framework中编译anroid工程并在模拟器上运行
  9. Javascript中的单例和模块模式
  10. sql server 性能优化
  11. 关于OCR身份证识别
  12. Python 编写自动化工具
  13. 大一新生HTML期末作业 学生个人网页设计作业 HTML5响应式个人简历网站模板 web前端网页制作课作业
  14. Git merge合并冲突 error: ‘merge‘ is not possible because you have unmerged files的解决方法
  15. 企业微信开发实战(二、OA审批之获取审批模版详情提交审批申请)
  16. 关于Oracle parallel(并行)的几个基本常识
  17. 【zyc的从零开始】20211012 运算符
  18. android Wifi连接及检测信号强度
  19. 数学大世界杂志数学大世界杂志社数学大世界编辑部2022年第7期目录
  20. Android屏幕解锁图案破解

热门文章

  1. Dockers更新镜像(保留原始数据)
  2. 基于HTML5+CSS+JacaScript和Java实现的校园校友APP前后台实现
  3. 华硕灵耀X双屏UX482EAR(UX4100EAR)原厂系统|带ASUS Recovery功能
  4. 免费开放的星座运势接口分享
  5. 看过来:在日本博士找工作有多难?
  6. Java面试遇到的问题(更新中---)
  7. 超实用的CAD技巧:一分钟教你如何将CAD转换成高清JPG图片
  8. 凝思操作系统配置IP地址方法
  9. 蒂法html5游戏,《蒂法h游戏》
  10. 基于Springboot的动漫论坛系统(源代码+数据库+ppt文档) 040