大型图形的A *算法,对缓存快捷方式有何想法?


69

我正在OpenStreetMap地图上编写快递/后勤仿真,并且已经意识到,如下图所示的基本A *算法对于大型地图(例如大伦敦)来说不够快。

http://i.imgur.com/u2tVpML.jpg

绿色节点对应于放置在开放集/优先级队列中的节点,由于数量巨大(整个地图大约为1-2百万),因此需要5秒钟左右的时间才能找到所描绘的路线。不幸的是,每条路线100ms大约是我的绝对极限。

当前,节点既存储在邻接表中,又存储在空间100x100 2D数组中。

我正在寻找可以权衡预处理时间,空间以及必要时优化路线以加快查询速度的方法。根据探查器,用于启发式成本的直线Haversine公式是最昂贵的函数-我已尽我所能优化了基本A *。

例如,我在考虑,如果我从2D数组的每个象限中选择一个任意的节点X并在每个象限之间运行A *,我可以将路由存储到磁盘以进行后续仿真。查询时,我只能在象限中​​运行A *搜索,以在预先计算的路径和X之间进行搜索。

是否有我上面描述的更完善的版本,或者也许是我应该采用的其他方法。非常感谢!

为了记录在案,以下是一些基准测试结果,用于任意加权启发式成本并计算10对随机选取的节点之间的路径:

Weight // AvgDist% // Time (ms)
1       1       1461.2
1.05    1       1327.2
1.1     1       900.7
1.2     1.019658848     196.4
1.3     1.027619169     53.6
1.4     1.044714394     33.6
1.5     1.063963413     25.5
1.6     1.071694171     24.1
1.7     1.084093229     24.3
1.8     1.092208509     22
1.9     1.109188175     22.5
2       1.122856792     18.2
2.2     1.131574742     16.9
2.4     1.139104895     15.4
2.6     1.140021962     16
2.8     1.14088128      15.5
3       1.156303676     16
4       1.20256964      13
5       1.19610861      12.9

令人惊讶的是,将系数增加到1.1几乎使执行时间减少了一半,同时保持了相同的路线。


我认为您应该尝试在该处
Wojciech Kulik

对算法的一种修改,它允许对每个路段进行加权(例如,八车道的高速公路每单位距离的成本为1,而未铺砌的土路的成本为50,并且介于两者之间的所有事物都将为……)可能的起点。尽管您要承担对从地图提供者处获得的所有线段进行分类的繁重任务,但如果这些线段尚未与之关联的合适数据……
twalberg 2015年

2
没有时间写一个完整的答案,但是最新的技术涉及寻找小的分隔符。不要满足于大致的结果-非常烦人的调试。
David Eisenstat

您是否对一对一路线或计算距离矩阵感兴趣?也许您可以在这里找到一些提示:stackoverflow.com/questions/430142
SebastianK

2
尝试使用“ Arc-Flags”预处理图形;这非常简单,还应该为您提供良好的加速效果。
user541686

Answers:


24

您应该能够通过权衡最优来使其更快。请参阅维基百科上的可容许性和最佳性

想法是使用一个epsilon值,该值将导致求解的结果不比1 + epsilon最佳路径倍,但将导致算法考虑的节点更少。请注意,这并不意味着返回的解决方案将始终是1 + epsilon最佳路径的倍数。这只是最坏的情况。我不知道它在实际中如何解决您的问题,但我认为值得探讨。

您将获得许多依赖于Wikipedia上这一思想的算法。我认为这是改进算法的最佳选择,它有可能在您的时间限制内运行,同时仍返回良好的路径。

由于您的算法确实在5秒内处理了数百万个节点,因此我假设您还使用二进制堆进行实现,对吗?如果手动实现它们,请确保将它们实现为简单数组,并且它们是二进制堆。


谢谢,我没有想到要尝试,它可以在不增加成本的情况下大大提高速度。只需将启发式成本乘以1.5,即可在200毫秒内获得91.8公里,而在5秒内则需要88.3公里。随着算法的运行,我将进一步尝试改变它。
drspa44

我尝试过使用.net SortedList和库中的基于数组的二进制堆,并且列表的速度略快。
drspa44

@ drspa44-如果您使用的是.net,是否尝试添加并行性?也许尝试将其添加到更新节点邻居的部分。我不确定是否会有所帮助,但是值得尝试用Parallel.For替换for循环。
IVlad 2015年

我确实在将要使用A *的实体上使用Parallel.ForEach,因此使用了所有CPU能力。我认为小规模并行化不会带来太大变化,尤其是在同步和开销方面。
drspa44

3
那里一定有问题。.netSortedList<TKey, TValue>是使用排序数组实现的,它具有O(n)插入/删除的功能(除非您始终在数组底部添加项目)。如果正确实现,堆就会O(log n)代替。它应该更快。在此处检查是否有一个很好的最大堆实现:referencesource.microsoft.com/#System.Core/System/Linq/Parallel/…对于A*,您需要相反的操作,但很容易适应。
tigrou

9

有针对此问题的专家算法,需要进行大量的预计算。从内存中,预计算将信息添加到图形中,A *使用该信息来生成比直线距离更精确的试探法。Wikipedia在http://en.wikipedia.org/wiki/Shortest_path_problem#Road_networks提供了许多方法的名称,并说集线器标签是领导者。快速搜索即可找到http://research.microsoft.com/pubs/142356/HL-TR.pdf。使用A *的较旧版本位于http://research.microsoft.com/pubs/64505/goldberg-sp-wea07.pdf

您真的需要使用Haversine吗?为了涵盖伦敦,我本以为您可以假设一个平坦的地球并使用毕达哥拉斯,或者将每个链接的长度存储在图中。


谢谢,我给那些人看书。即使很短的距离,毕达哥拉斯也可以离开。切换出去经常导致次优路线的出现。
drspa44

在这里必须杀死哈弗斯汀以使地球变得几乎平坦。已投票。
鹿猎人


2
@ drspa44-您必须重新投影地图(想想局部居中的墨卡托横向投影)!
鹿猎人

有一个简单的平面上的投影:github.com/graphhopper/graphhopper/blob/master/core/src/main/...也看到评论的链接
Karussell

7

Microsoft Research在该主题上写了一篇非常出色的文章:

http://research.microsoft.com/zh-cn/news/features/shortestpath-070709.aspx

原始文件位于(PDF):

http://www.cc.gatech.edu/~thad/6601-gradAI-fall2012/02-search-Gutman04siam.pdf

本质上,您可以尝试以下几种方法:

  1. 从源和目的地开始。从源向外遍历到目的地时,这有助于最大程度地减少浪费的工作量。
  2. 使用地标和高速公路。本质上,在每个地图中找到一些通常采用的位置,并执行一些预计算以确定如何在这些点之间高效导航。如果您可以找到从源到地标,然后再到其他地标,再到目的地的路径,则可以快速找到一条可行的路线并从那里进行优化。
  3. 探索诸如“到达”算法之类的算法。通过最小化寻找有效路径所需考虑的顶点数量,这有助于最小化遍历图形时要做的工作量。

谢谢。我确实读过该文章,今天我正试图提出一些方法来实施某种形式的地标性方法。对我来说,问题不在于如何根据我拥有的数据选择地标。对我来说,高速公路也不是那么容易识别。我确实尝试了覆盖率,但发现性能提升可忽略不计。从Microsoft的图像来看,双向Djikstra和A *似乎并没有比我现在所讲的快很多,尽管我还没有实现。
drspa44

第2步称为收缩层次结构。
MSalters 2015年

@MSalters与地标,它是另一种野兽-> ALT
Karussell

6

GraphHopper还要做两件事,以实现快速,无启发式和灵活的路由(注意:我是作者,您可以在此处在线尝试)

  1. 不太明显的优化是避免OSM节点与内部节点的1:1映射。相反,GraphHopper仅将结点用作节点,并节省了大约1/8的遍历节点。
  2. 它具有适用于A *,Dijkstra或例如一对多Dijkstra的高效工具。这样一来,整个德国境内的路程就可能不到1秒。A *的(无启发式)双向版本使其速度更快。

因此,应该可以为您提供前往大伦敦的快速路线。

另外,默认模式是速度模式,它使所有内容都快一个数量级(例如,欧洲宽频路线为30毫秒),但灵活性较差,因为它需要进行预处理(收缩层次结构)。如果您不喜欢它,只需禁用它,然后进一步微调所包含的汽车街道,或者更好地为卡车创建新的配置文件-例如,排除服务街道和轨道,这将使您进一步提高30%。与任何双向算法一样,您可以轻松实现并行搜索。


4

我认为有必要用“象限”解决您的想法。更严格地说,我将其称为低分辨率路线搜索。

您可以选择足够靠近的X个连接的节点,并将它们视为单个低分辨率节点。将您的整个图分成这样的组,您会得到一个低分辨率的图。这是一个准备阶段。

为了计算从源到目标的路由,首先确定它们所属的低分辨率节点,然后找到低分辨率路由。然后通过在高分辨率图上找到路由来改善结果,但是将算法仅限制于属于该低分辨率路由的低分辨率节点的节点(可选地,您也可以考虑相邻的低分辨率节点,直到某个深度)。

这也可以推广到多种分辨率,而不仅仅是高/低。

最后,您应该获得一条足够接近最佳路线的路线。它是局部最优的,但在某种程度上可能比全局最优要差一些,这取决于分辨率的跃迁(即,当一组节点定义为单个节点时所做的近似)。


谢谢,如果我遵循这种方法,我会记住这个答案。
drspa44

3

这里有数十种A *变体可以满足要求。但是,您必须考虑用例。

  • 您的内存(以及缓存)是否受到限制?
  • 您可以并行搜索吗?
  • 您的算法实现是否仅在一个位置使用(例如,大伦敦,而不是纽约市,孟买或其他任何地方)?

我们无法知道您和您的雇主所不知道的所有详细信息。因此,您的第一站应该是CiteSeer或Google Scholar:寻找与您一样具有一般约束条件的寻路论文。

然后向下选择三种或四种算法,进行原型设计,测试它们如何扩大规模并对其进行微调。您应该记住,您可以根据点之间的距离,剩余时间或任何其他因素,在同一大型寻路例程中组合各种算法。

就像已经说过的那样,基于目标区域的小规模下降,Haversine可能是您第一步,可以节省宝贵的时间来进行昂贵的触发评估。注意:我不建议在经度,纬度坐标中使用欧几里得距离-将地图重新​​投影到中心附近的例如横向墨卡托投影中,并以码或米为单位使用笛卡尔坐标!

预计算是第二个问题,而更改编译器可能是一个显而易见的第三个想法(切换到C或C ++-有关详细信息,请参见https://benchmarksgame.alioth.debian.org/)。

额外的优化步骤可能包括摆脱动态内存分配,并在节点之间使用高效索引进行搜索(请考虑R树及其派生/替代)。


3

我在一家主要的导航公司工作,所以我可以自信地说100 ms应该可以使您从伦敦到雅典的路线,即使是在嵌入式设备上。大伦敦对我们来说将是一张测试地图,因为它的体积很小(很容易放入RAM中-这实际上不是必需的)

首先,A *完全过时了。它的主要好处是“技术上”不需要预处理。实际上,无论如何,您都需要预处理OSM映射,这是没有意义的。

极大提高速度的主要技术是弧形标志。如果将地图分为5x6部分,则可以为每个部分分配32位整数中的1位位置。现在,您可以决定每个边缘行进时,它是否是有史以来有用部分{X,Y}从另一个部分。道路经常是双向的,这意味着只有两个方向之一才有用。因此,两个方向之一已将该位置1,另一个已清除。这似乎并不是真正的好处,但是这意味着在许多路口上,您将要考虑的选择数量从2个减少到仅1个,并且只需要一点操作。


对于伦敦来说,一个主要优点是您也可以在桥上设置这些位。A *不必要地过河并卡在另一岸可能会遭受很多折磨。
MSalters 2015年

0

通常,A *会带来过多的内存消耗,而不是浪费时间。

但是,我认为首先仅使用属于“大街道”的节点进行计算可能会很有用,您通常会选择在一条小巷上行驶一条高速公路。

我想您可能已经将此功能用于权重功能,但是如果您使用某些优先级队列来确定接下来要测试哪个节点以进一步行驶,则可以更快。

另外,您可以尝试将图简化为仅包含低成本边缘一部分的节点,然后找到从这些节点开始/结束到最接近这些节点的方法。因此,您有2条路径,从起点到“大街”,再到“大街”到终点。现在,您可以在简化图中计算属于“大街道”的两个节点之间的最佳路径。


-1

老问题,但是:

尝试使用不同的堆,即“二进制堆”。最好的渐近复杂性堆是Fibonacci Heap,它的Wiki页面有一个很好的概述:

https://zh.wikipedia.org/wiki/Fibonacci_heap#Summary_of_running_times

请注意,二进制堆具有更简单的代码,并且它是在数组上实现的,并且数组的遍历是可预测的,因此现代CPU执行二进制堆操作速度快得多。

但是,给定足够大的数据集,由于其复杂性,其他堆将胜过二进制堆...

这个问题似乎足够大。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.