A*是一个比较经典的启发式寻路算法。是基于dijkstra算法,但是加入了启发函数,使路径搜索效率更高。实现起来很简单。不过要做到通用性高,比如支持各种不同类型的地图,甚至不仅仅是地图,而是个图结构如解决拼图游戏N-puzzle会用到的,就需要多花点心思。用C++实现的话,可以使用模板来适应不同的需要。也可以使用类继承。

  template <typename NodeType, typename CostType, typename Heuristic>
static vector<NodeType> search(Map<NodeType, CostType> &map,
NodeType start, NodeType goal,
Heuristic heuristic) {
map.initialize(start, goal);
map.open_node(start, , heuristic(start, goal), start); // Get started. while (map.open_node_available()) {
NodeType top_node = map.close_front_open_node(); if (map.nodes_equal(top_node, goal))
return map.get_path(top_node); // Stop and return the path found. const vector<Edge<NodeType, CostType>> &&edges = map.edges(top_node);
for (auto edge : edges) { // For each qualified edge evaluate target node.
NodeType node_to_evaluate = edge.to_;
CostType g = map.current_cost(top_node) + edge.cost_;
CostType h = heuristic(node_to_evaluate, goal); if (map.node_unexplored(node_to_evaluate)) {
map.open_node(node_to_evaluate, g, h, top_node);
} else if (map.cost_greater(map.current_cost(node_to_evaluate), g)) {
if (map.node_open(node_to_evaluate)) {
map.increase_node_priority(node_to_evaluate, g, h, top_node);
} else { // Won't reach here if heuristic is consistent(monotone).
map.reopen_node(node_to_evaluate, g, h, top_node);
}
}
}
} return vector<NodeType>(); // No path found. Return an empty path.
}

很久以前在大学里开发自制的即时战略游戏,第一次实现这个算法。不过那个时候互联网还不流行,很难找到相关资料,所以用现在的眼光看实现得不够好。尤其是open列表,只是用了个有序链表。现在看来,可以使用一个优先队列(priority queue),毕竟每次从open列表只需要取第一个元素,其他元素的次序并不重要。C++的STL提供了一个优先队列,不过很不幸,并没有提供改变某个元素优先度的操作。所以,要么自己实现一个优先队列,要么在STL的基础上添加这个功能。如果看下STL的源代码就会发现STL的优先队列实际上是一个二叉堆(binary heap)。所以,只要在这个基础上添加个函数percolate_up()函数就可以了。当然,偷懒一点,修改元素优先度后再用std::make_heap()重新生成堆也可以替代,不过效率就差了。

// Percolate up an element at the index specified.
template<typename Container, typename ElementType, typename LessPriority>
static void percolate_up(Container &container, int index, LessPriority less_priority) {
auto value = container[index];
int hole = index;
int parent = (hole - ) / ;
while (hole > && less_priority(container[parent], value)) {
container[hole] = container[parent];
hole = parent;
parent = (hole - ) / ;
}
container[hole] = value;
}

再改进一下,可以用HOT(heap on top)来优化。也就是建立一个数据结构,只在最上面使用优先队列,而其余的元素则放在其他的容器里,比如分成很多个bucket的vector。这样做的好处是可以缩小优先队列的规模,那些不大可能被open的元素则不进队列,除非队列已经排空需要把一个bucket转化为新的优先队列。

另外一项技术,就是Jump Point Search(JPS或者所谓的跳点搜索)。这是一个近年来发现的高效寻路算法。不过有一个限制就是只能在规则的格子地图上寻路,而且图上的点或边不能带权重,也就是不能有复杂的地形,只支持平坦和障碍两种地形。其思想就是跳过矩形平坦区域的大量对称路径,只寻找所谓的跳跃点,作为搜索的节点。这样做的好处是裁剪了矩形区域内大量的节点,使open list中的节点数相对较少。不过JPS有个缺点是每生成一个节点,也就是要找到一个跳跃点,相比较一般的A*算法,是比较昂贵的。幸好通常来说,得到的收益更多些。所以,在适用的情况下,还是推荐使用JPS的。

具体的实现,主要有两部分。第一部分,从open list中取一个最佳节点,然后从几个特定方向展开搜索,把每个方向得到的跳跃点,加入open list里。第二部分,就是找到一个跳跃点。

对于起始点,可以向所有方向展开搜索。对于其他节点,要看父节点指向本节点的方向,向所有自然邻居和被迫邻居节点方向进行搜索。
例如下图的例子,对于节点n和父节点p和障碍x,+是n的自然邻居,也就是说从p到n到+是最佳路径。如果x不是障碍,从p到n到-不是最佳路径,因为从p到x到-最近。但是如果x是障碍,那么从p到n到-就是最佳路径了,所以这时候称-为被迫邻居。

- + +
x n +
p x -

以上是n和p对角的例子。还有种情况是n和p是直线:

x x -
p n +
x x -

搜寻跳跃点是递归进行的。首先判断一个节点是否是跳跃点。如果这个点有被迫邻居,那么这个节点就是跳跃点。第二种情况,如果这个节点是目的地节点那么也当作跳跃点。如果不是,那么就递归地沿本来方向继续搜寻下去。对于对角方向要额外多做两步,即先对其相应的(左右两个)直线方向进行搜索,如果找到跳跃点,就把自身也当作跳跃点返回。如果直线没找到,那么就一样继续按对角方向递归寻找跳跃点,并返回那个跳跃点。

以下是使用A*算法和JPS算法搜索一个10x10的格子地图得到的结果比较(x是障碍,S是起始点,G是终点,@是最佳路径,o是open节点,-是closed节点)

大致可以看出JPS是如何通过jump point跳过矩形区域的。

A*:

  ooox-S--
oo@Gx-@--
oo@xxxx@--
o@-x--xx@-
@xxx---x@-
-@-x-x-x@-
o-@x-x-x@-
o--@@x-x@-
oo---@@@--
oo-------
 

JPS:

     x S
@Gx @
xxxx
x xx
@xxx - x
x-x-x
x x x
@@x-x@
@ @

代码和示例放在了github上:https://github.com/silmerusse/fudge_pathfinding

寻路算法A*, JPS(跳点搜索)的一些杂谈的更多相关文章

  1. python实现的跳点寻路算法(JPS)

    原理参考论文 代码已提交到git(https://github.com/YYRise/find_path/blob/master/jps.py)

  2. JPS/JPS+ 寻路算法

    目录 概念 强迫邻居(Forced Neighbour) 跳点(Jump Point) JPS 寻路算法(Jump Point Search) 实现原理 示例过程 JPS+(Jump Point Se ...

  3. A*寻路算法探究

    A*寻路算法探究 A*算法常用在游戏的寻路,是一种静态网路中求解最短路径的搜索方法,也是解决很多搜索问题的算法.相对于Dijkstra,BFS这些算法在复杂的搜索更有效率.本文在U3D中进行代码的测试 ...

  4. 用简单直白的方式讲解A星寻路算法原理

    很多游戏特别是rts,rpg类游戏,都需要用到寻路.寻路算法有深度优先搜索(DFS),广度优先搜索(BFS),A星算法等,而A星算法是一种具备启发性策略的算法,效率是几种算法中最高的,因此也成为游戏中 ...

  5. A*寻路算法的探寻与改良(三)

    A*寻路算法的探寻与改良(三) by:田宇轩                                        第三分:这部分内容基于树.查找算法等对A*算法的执行效率进行了改良,想了解细 ...

  6. 关于A*寻路算法的认识

    最近要参加学校的APP比赛,我们组做的是一个3D迷宫的小APP,我负责的是迷宫的生成与寻路. 寻路算法选择的是A*寻路算法,具体参考的是下面的这篇博客. 本文主要是谈谈自己对A*算法的理解,具体细节, ...

  7. A*寻路算法的探寻与改良(二)

    A*寻路算法的探寻与改良(二) by:田宇轩                                                     第二部分:这部分内容主要是使用C语言编程实现A*, ...

  8. A*寻路算法的探寻与改良(一)

    A*寻路算法的探寻与改良(一) by:田宇轩                                                                    第一部分:这里我们主 ...

  9. C#实现AStar寻路算法

    AStar寻路算法是一种在一个静态路网中寻找最短路径的算法,也是在游戏开发中最常用到的寻路算法之一:最近刚好需要用到寻路算法,因此把自己的实现过程记录下来. 先直接上可视化之后的效果图,图中黑色方格代 ...

随机推荐

  1. IOS7开发~新UI学起(四)

    本文转载至 http://blog.csdn.net/lizhongfu2013/article/details/9166193 1.UITableView: UITableViewDelegate ...

  2. Xamarin绑定微信SDK 实现分享功能

    从开始做这一块的工作开始,就开始找各种的资料,最后还是老老实实的去看官方文档. 对于Xamarin.Android的绑定属于纯jar的绑定,这个难度较小,添加Bindings Library,将lib ...

  3. Android存储Json到本地,和读取本地Json

    /** * 保存json到本地 * @param mActivity * @param filename * @param content */ public static File dir = ne ...

  4. 170410、java Socket通信的简单例子(TCP)

    服务端代码: package com.bobohe.socket; import java.io.*; import java.net.*; import java.applet.Applet; pu ...

  5. PAT 甲级 1104 sum of Number Segments

    1104. Sum of Number Segments (20) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CAO, Pen ...

  6. IPTABLES简介

    iptables防火墙工作原理 简介:iptables防火墙工作在网络层,针对TCP/IP数据包实施过滤和限制,iptables防火墙基于内核编码实现,具有非常稳定的性能和高效率: iptables属 ...

  7. CLR via 笔记4.2 类型转换 is 与 as 区别

    is 和 as 操作符是用来进行强制类型转换的 is : 检查一个对象是否兼容于其他指定的类型,并返回一个Bool值,永远不会抛出异常 object o = new object(); if (o i ...

  8. 剑指Offer——孩子们的游戏(圆圈中最后剩下的数)

    题目描述: 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机 ...

  9. Flask视图函数与模板语法

    1.Django中的CBV模式 ​ 2.Flask中的CBV和FBV def auth(func):     def inner(*args, **kwargs):         result =  ...

  10. 为什么说”人生苦短,我用python“?

    本文不扯什么大道理,只是先介绍Python的背景,然后从实用的角度出发举一两个真实栗子. ​ 首先要想了解要一门语言的好坏,或者为什么招程序员喜欢(卧槽,原来程序员喜欢不是女朋友?)我们的先从语言的产 ...