I am looking for a pathfinding algorithm to use for an AI controlling an entity in a 2D grid that needs to find a path from A to B. It does not have to be the shortest path but it needs to be calculated very fast. The grid is static (never changes) and some grid cells are occupied by obstacles.
I'm currently using A* but it is too slow for my purposes because it always tries to calculate the fastest path. The main performance problem occurs when the path does not exist, in which case A* will try to explore too many cells.
Is there a different algorithm I could use that could find a path faster than A* if the path doesn't have to be the shortest path?
Thanks,
Luminal
Assuming your grid is static and doesn't change. You can calculate the connected components of your graph once after building the grid.
Then you can easily check if source and target vertex are within a component or not. If yes, then execute A*, if not then don't as there can't be a path between the components.
You can get the connected components of a graph using BFS or DFS.
To find a path instead of the shortest path, use any graph traversal (e.g. depth-first or best-first). It won't necessarily be faster, in fact it may check many more nodes than A* on some graphs, so it depends on your data. However, it will be easier to implement and the constant factors will be significantly lower.
To avoid search for a path when there is none, you could create disjoint sets (once after you built the graph) to very quickly check whether two given points are connected. This takes linear space and linear time to build, and lookup takes amortized practically-constant time, but you still need to run your full algorithm at times, as it will only tell you whether there is a path, not where that path goes.
If you're already building data structures beforehand, and have a bit more time and space to trade for instant shortest paths at run-time, you can have your cake and eat it too: The Floyd-Warshall algorithm gives you all shortest paths in comparatively modest O(|V|^3)
time, which is the most bang for the buck considering there are |V|² (start, destination) pairs. It computes a |V| * |V|
matrix, which could be a bit large, but consider that this is an integer matrix and you only need |V| * |V| * log_2 |V|
bits (for example, that's 1.25 MiB for 1024 vertices).
You can use either DFS or BFS since you just want to know if the two vertices are connected. Both algorithms run in O(|V|)
where V
is the set of all vertices in the graph.
Use any of this two algorithms if your heuristic takes some non trivial time to get computed, otherwise I think A* should run similarly or better than DFS or BFS.
As another option you can use the Floyd-Warshall algorithm (O(V^3)
) to calculate, after you create the grid, the shortest distance path between each pair of vertices, thus doing all the heavy lifting at the start of the simulation and then have stored all shortest paths for O(1) access in a hash, or if this turns out to be too memory explosive you can just keep a matrix next
such that next[i][j]
stores the vertex that we must take to go from vertex i
to vertex j
. Thus we can build the path from i
to j
as (i, k1=next[i][j]), (k1, k2=next[k1][j]) ... (kr, j)
If the graph is small enough, you can precompute all shortest paths using the Floyd-Warshall algorithm. This takes O(|V|²)
memory for storing the paths, and O(|V|³)
time for the precomputation.
Obviously this is not an option for very large graphs. For those you should use Thomas's answer and precompute the connected components (takes linear time and memory) to avoid the most expensive A* searches.
A*, BFS, DFS, and all other search algorithms will have to check exactly the same number of nodes when there is no path, so "use DFS first" is not a good answer. And using Floyd-Warshall is completely unnecessary.
For static graphs, the solution is simple; see @Thomas's answer. For non-static graphs, the problem is more complicated; see this answer for a good algorithm.
In case your maze never changes and any path that exists exists for ever, could you not use a mapping algorithm wich would find "regions" of your maze and store those in some format? The memory usage would be linear with the amount of nodes or cells and speed is the speed of accessing and comparing two elements of an array.
The computing (spliting to regions) would probably be more time consuming but it's done once in the beginning. Maybe you could adapt some flood fill algorithm for this purpose?
Not sure if it's clear from the above, but i think an example is always ahelp. I hope you'll forgive me using PHP syntax.
example (maze 5x5) ([]
marks an obstacle):
0 1 2 3 4
+----------+
0 |[][] 2 |
1 | [][] |
2 | [] []|
3 | 1 [] |
4 | [] |
+----------+
indexed regions (using numeric hash instead of 'x:y' may be better):
$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
);
then your task is only to find whether your start and end point are both in the same region:
if ( $regions[$startPoint] == $regions[$endPoint] )
pathExists();
Now if i am not mistaken the A* complexity (speed) depends on the distance between start and end points (or length of solution), and that may perhaps be used to speed up your search.
I would try to create some "junction nodes" (JN) in the maze. Those could be located on a function (to know fast where the nearest JN is) and you would have paths between all neighbouring JN precalculated.
So then you need only search for solution from startpoint to nearest JN and from endpoint to it's nearest JN (where all of them are in the same region (the above)). Now i can see a scenario (if the function isn't well chosen with regards to the comlexity of the maze) that has several regions, some of which may have no JN at all or that all your JN fall onto obstacles in the maze... So it may be better to manually define those JN if possible or make this JN placing function non-trivial (taking in consideration the area of each region).
Once you reach the JN you may have the paths between them either indexed (to fast retrieve predefined path between start and end JN) or perform another A* pathfinding, except this time only on the set of "junction nodes" - as there's much less of those this path search between JN will be faster.
You may consider using an Anytime A* algorithm (ANA* or other variants).
These will start by performing a greedy best first search to find an initial path.
It will then make incremental improvements by running with the heuristic function increasingly less less weighted.
You can call off the search at any time and get its best path found so far.