我正在寻找一种寻路算法,用于AI控制2D网格中的实体,需要找到从A到B的路径。它不一定是最短的路径,但需要非常快速地计算。网格是静态的(永远不会改变),一些网格单元被障碍物占用。
我目前正在使用A *,但它对我的目的来说太慢了,因为它总是试图计算最快的路径。当路径不存在时会出现主要的性能问题,在这种情况下,A *将尝试探索太多的单元。
如果路径不必是最短路径,我可以使用哪种算法可以找到比A *更快的路径?
谢谢,
管腔
答案 0 :(得分:9)
假设您的网格是静态的并且不会更改。在构建网格后,您可以计算图表的连接组件。
然后,您可以轻松检查源和目标顶点是否在组件内。如果是,则执行A *,否则执行A *,因为组件之间不能存在路径。
您可以使用BFS或DFS获取图表的连接组件。
答案 1 :(得分:4)
要查找 a 路径而不是最短路径,请使用任何图形遍历(例如深度优先或最佳优先)。它不一定会更快,事实上它可能会在某些图表上检查比A *更多的节点,因此它取决于您的数据。但是,它将更容易实施,并且常数因素将显着降低。
为了避免在没有路径时搜索路径,您可以创建disjoint sets(在构建图形后一次) very 快速检查两个给定点是否已连接。这需要线性空间和线性时间来构建,并且查找实际上是按时间分摊,但是您仍需要运行完整算法,因为它只会告诉您是否存在路径,不是那条路走的路。
如果你已经预先构建了数据结构,并且在运行时有更多的时间和空间来交换即时最短路径,你可以吃蛋糕并吃掉它: Floyd-Warshall algorithm在相对适度的O(|V|^3)
时间内为您提供所有最短路径,考虑到有| V |²(起始,目的地)对,这是降压最大的路径。它计算|V| * |V|
矩阵,它可能有点大,但考虑到这是一个整数矩阵,你只需要|V| * |V| * log_2 |V|
位(例如,对于1024个顶点,这是1.25 MiB)。
答案 2 :(得分:2)
您可以使用DFS或BFS,因为您只想知道这两个顶点是否已连接。这两种算法都在O(|V|)
中运行,其中V
是图中所有顶点的集合。
如果您的启发式算法需要一些非常重要的时间来计算,请使用这两种算法中的任何一种,否则我认为A *应该与DFS或BFS类似或更好地运行。
作为另一个选项,您可以使用Floyd-Warshall algorithm(O(V^3)
)在创建网格后计算每对顶点之间的最短距离路径,从而在开始时完成所有繁重的工作模拟然后已经存储了哈希中O(1)访问的所有最短路径,或者如果事实证明存在爆炸性,那么你可以保留一个矩阵next
,以便next[i][j]
存储我们必须从顶点i
到顶点j
的顶点。因此,我们可以将i
到j
的路径构建为(i, k1=next[i][j]), (k1, k2=next[k1][j]) ... (kr, j)
答案 3 :(得分:1)
如果图表足够小,您可以使用Floyd-Warshall algorithm预先计算所有最短路径。这需要O(|V|²)
内存来存储路径,并O(|V|³)
时间进行预计算。
显然,对于非常大的图表,这不是一个选项。对于那些你应该使用托马斯的答案并预先计算连接的组件(需要线性时间和内存)以避免最昂贵的A *搜索。
答案 4 :(得分:0)
A *,BFS,DFS和所有其他搜索算法在没有路径时必须检查完全相同数量的节点,因此“首先使用DFS”不是一个好的答案。使用Floyd-Warshall 完全是不必要的。
对于静态图,解决方案很简单;看@ Thomas的回答。对于非静态图,问题更复杂;请参阅this answer了解一个好的算法。
答案 5 :(得分:0)
如果您的迷宫永远不会改变并且任何存在的路径永远存在,您是否可以使用映射算法找到迷宫的“区域”并以某种格式存储它们?内存使用量与节点或单元格数量呈线性关系,速度是访问和比较数组中两个元素的速度。
计算(拆分到区域)可能会耗费更多时间,但在开始时就完成了一次。也许你可以为此目的调整一些洪水填充算法?
不确定从上面是否清楚,但我认为一个例子总是有帮助的。我希望你能原谅我使用PHP语法。
示例(迷宫5x5)([]
标志着障碍):
0 1 2 3 4
+----------+
0 |[][] 2 |
1 | [][] |
2 | [] []|
3 | 1 [] |
4 | [] |
+----------+
索引区域(使用数字哈希而不是'x:y'可能更好):
$regions=array(
'0:1'=>1,'0:2'=>1,'0:3'=>1,'0:4'=>1,'1:2'=>1,'1:3'=>1,'1:4'=>1,
'2:0'=>2,'3:0'=>2,'3:1'=>2,'3:2'=>2,'3:3'=>2,'3:4'=>2,'4:0'=>2,
'4:1'=>2,'4:3'=>2,'4:4'=>2
);
那么你的任务只是找出你的起点和终点是否都在同一个区域:
if ( $regions[$startPoint] == $regions[$endPoint] )
pathExists();
现在,如果我没有弄错,A *复杂度(速度)取决于起点和终点之间的距离(或解决方案的长度),这可能会用于加快搜索速度。
我会尝试在迷宫中创建一些“结节点”( JN )。这些可以位于一个函数上(快速了解最近的 JN 的位置),并且您将在所有相邻的 JN 之间预先计算路径。
那么你只需要搜索从起点到最近的 JN 的解决方案,并从端点到它最近的 JN (其中所有这些都在同一区域内(上面) ))。现在我可以看到一个场景(如果关于迷宫的复杂性没有很好地选择该功能),它有几个区域,其中一些可能根本没有 JN 或者你的所有< em> JN 落入迷宫中的障碍......所以如果可能的话,手动定义那些 JN 可能会更好,或者让这个 JN 放置功能非微不足道的(考虑到每个地区的面积)。
一旦到达 JN ,您可能会将它们之间的路径编入索引(以快速检索开始和结束之间的预定义路径 JN )或执行另一个A *寻路,除了这一次只在“连接节点”的集合上 - 因为 JN 之间的路径搜索将会更快。
答案 6 :(得分:0)
您可以考虑使用Anytime A *算法(ANA *或其他变体)。
这些将从执行贪婪的最佳第一次搜索开始,以找到初始路径。
然后,通过使用启发式函数运行越来越少的权重,它将进行渐进式改进。
您可以随时取消搜索并找到目前为止找到的最佳路径。