我正在OpenStreetMap地图上写一个快递/物流模拟,并且已经意识到如下图所示的基本A *算法对于大型地图(如大伦敦)来说不够快。
绿色节点对应于放置在开放集/优先级队列中的绿色节点,并且由于数量巨大(整个地图大约为1-2百万),需要5秒左右才能找到所描绘的路线。不幸的是,每条路线100毫秒是我的绝对限制。
目前,节点存储在邻接列表和空间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几乎将执行时间减半,同时保持相同的路线。
答案 0 :(得分:23)
你应该能够通过权衡最优性来加快速度。请参阅维基百科上的Admissibility and optimality。
这个想法是使用epsilon
值,这将导致解决方案不超过最佳路径的1 + epsilon
倍,但这将导致算法考虑更少的节点。请注意,这并不意味着返回的解决方案始终是最佳路径的1 + epsilon
倍。这只是最糟糕的情况。我不确切地知道它在实践中会如何表现你的问题,但我认为值得探讨。
在维基百科上,您会获得许多依赖此想法的算法。我相信这是改进算法的最佳选择,并且它有可能在您的时间限制内运行,同时仍然可以返回良好的路径。
由于你的算法确实在5秒内处理了数百万个节点,我假设你也使用二进制堆来实现,对吗?如果您手动实现它们,请确保它们是作为简单数组实现的,并且它们是二进制堆。
答案 1 :(得分:9)
这个问题有专门的算法可以进行大量的预先计算。从内存中,预计算将信息添加到A *用于产生比直线距离更准确的启发式的图形。维基百科在http://en.wikipedia.org/wiki/Shortest_path_problem#Road_networks给出了许多方法的名称,并表示Hub Labeling是领导者。快速搜索http://research.microsoft.com/pubs/142356/HL-TR.pdf。较旧的一个使用A *,位于http://research.microsoft.com/pubs/64505/goldberg-sp-wea07.pdf。
你真的需要使用Haversine吗?为了覆盖伦敦,我原本以为你可以假设一个平坦的地球并使用毕达哥拉斯,或者存储图中每个链接的长度。
答案 2 :(得分:7)
微软研究院就这个主题发表了一篇非常好的文章:
http://research.microsoft.com/en-us/news/features/shortestpath-070709.aspx
原始论文在这里(PDF):
http://www.cc.gatech.edu/~thad/6601-gradAI-fall2012/02-search-Gutman04siam.pdf
基本上你可以尝试一些事情:
答案 3 :(得分:5)
GraphHopper做更多快速,无启发和灵活的路由(请注意:我是作者,您可以在线试用here)
因此,应该可以为您提供更快捷的伦敦路线。
此外,默认模式是速度模式,它使所有内容的速度更快(例如,对于欧洲宽路线为30ms),但灵活性较低,因为它需要预处理(Contraction Hierarchies)。如果您不喜欢这样,只需禁用它,并进一步微调所包含的汽车街道,或者更好地为卡车创建新的配置文件 - 例如排除服务街道和轨道,这将使你进一步提高30%。与任何双向算法一样,您可以轻松实现并行搜索。
答案 4 :(得分:4)
我认为用#34象限制定你的想法是值得的。更严格的是,我将其称为低分辨率路径搜索。
您可以选择足够接近的X个连接节点,并将它们视为单个低分辨率节点。将整个图表划分为这样的组,您将获得一个低分辨率图表。这是一个准备阶段。
为了计算从源到目标的路由,首先要识别它们所属的低分辨率节点,然后找到低分辨率路径。然后通过在高分辨率图上查找路径来改善您的结果,但是将算法仅限制为属于低分辨率路径的低分辨率节点的节点(可选地,您也可以将相邻的低分辨率节点考虑到某个深度) )。
这也可以推广到多种分辨率,而不仅仅是高/低。
最后你应该得到一条足够接近最佳的路线。它在局部是最优的,但在某种程度上可能比全局更差,这取决于分辨率跳跃(即当一组节点被定义为单个节点时所做的近似)。
答案 5 :(得分:3)
这里有数十种可能符合要求的A *变种。但是,你必须考虑你的用例。
我们无法知道您和您的雇主所知的所有细节。因此,您的第一站应该是CiteSeer或Google学术搜索:查找使用与您相同的一般约束条件处理路径查找的论文。
然后向下选择三到四个算法,进行原型设计,测试它们如何扩展和微调它们。您应该记住,您可以根据点之间的距离,剩余时间或任何其他因素,在同一个宏寻路程序中组合各种算法。
正如已经说过的那样,基于您的目标区域的小规模,降低Haversine可能是您在昂贵的三角评估上节省宝贵时间的第一步。注意:我不建议在lat,lon坐标中使用欧几里德距离 - 将地图重新投影到例如在中心附近横向墨卡托,并使用码数或米的笛卡尔坐标!
预计算是第二个,更改编译器可能是一个明显的第三个想法(切换到C或C ++ - 有关详细信息,请参阅https://benchmarksgame.alioth.debian.org/。)
额外的优化步骤可能包括摆脱动态内存分配,并在节点之间使用有效的索引进行搜索(想想R-tree及其衍生物/替代品)。
答案 6 :(得分:3)
我在一家主要的导航公司工作,所以我可以放心地说,即使在嵌入式设备上,100毫秒也可以让你从伦敦到雅典。大伦敦将是我们的测试地图,因为它很方便(很容易适合RAM - 这实际上不是必需的)
首先,A *完全过时了。它的主要好处是它“技术上”不需要预处理。实际上,无论如何都需要预先处理OSM地图,这是一种毫无意义的好处。
为您提供巨大的速度提升的主要技巧是弧标志。如果将地图划分为5x6个部分,则可以为每个部分分配32位整数的1位位置。现在,您可以确定每个边缘是否在从其他部分旅行到部分{X,Y}
时是否有用。通常,道路是双向的,这意味着两个方向中只有一个是有用的。因此,这两个方向中的一个已设置该位,另一个已清除。这可能看起来不是真正的好处,但这意味着在许多交叉点上,您可以将选择的数量从2减少到只有1,这只需要一位操作。
答案 7 :(得分:0)
通常A *伴随着太多的内存消耗,而不是时间的浪费。
但是我认为首先只计算属于“大街”的节点,你会选择通常在一条小巷子里的高速公路。
我猜你可能已经将它用于你的体重功能,但如果你使用一些优先级队列来决定接下来要测试哪个节点以便进一步旅行,你可以更快。
此外,您可以尝试将图形缩小为仅属于低成本边缘的节点,然后找到从开始/结束到最接近这些节点的方法。 所以从开始到“大街”和“大街”到结束都有2条路径。 您现在可以计算缩小图中“大街道”一部分的两个节点之间的最佳路径。
答案 8 :(得分:-1)
老问题,但是:
尝试使用"二进制堆"的不同堆。 '最佳渐近复杂堆'肯定是Fibonacci Heap和它的维基页面得到了很好的概述:
https://en.wikipedia.org/wiki/Fibonacci_heap#Summary_of_running_times
请注意,二进制堆具有更简单的代码,并且它可以通过数组实现并且遍历数组是可预测的,因此现代CPU可以更快地执行二进制堆操作 。
然而,鉴于数据集足够大,其他堆将胜过二进制堆,因为它们的复杂性......
这个问题看起来像数据集足够大。