Find all simple path from node A to node B in direct weighted graph with the sum of weighs less a certain value?
Asked Answered
S

1

1

I have a directed weighted graph G=(V,E), which may have loops.

I am trying to determine the best time efficient algorithm to accomplish task: to find all simple path in G between source and target node with total weight of edges in this path less than certain value (for convenience we denote this value as PATH_WEIGHT_LIMIT)

All weights is positive and can be float.

So, a prototype of my function will be:

def find_paths(G, source, target, path_weight_limit) 

Result paths may overlap, its fine.

Much like those discussed here, e.g.:

  1. algorithm for finding NUMBER of distinct paths from A to B in weighted, directed, cyclic graph
  2. find all simple path in UNDERICTED graph (NP-problem)

But I didn't find an algorithm to solve this specifically task that I posed above to find all simple path in G (directed, weighted, cyclic) between source and target node with total weight of edges in this path less than certain value

I think that should be used modified DFS algorithm with a focus on weights of current part of path. But I think it is not effective and maybe there are better algorithms to solve this.

Seyler answered 21/4, 2016 at 9:41 Comment(3)
I think a simple DFS or BFS is fine. Heuristics as in Dijkstra's or A* don't help much, as you want to find all the path that qualify, anyway. Just make sure to (a) abort the current path if it contains a loop or is too long, and (b) collect the paths in a list instead of just returning the first one.Aliped
@Aliped "is too long" you mean current total weight > path_weight_limit?Seyler
For an n-vertex digraph, in the worst case this can return all possible sequences of up to n distinct vertices as paths: Consider the input consisting of n vertices with a weight-1 edge in both directions between every pair of vertices, and the weight limit is n.Willowwillowy
E
3

Theoretically, every node has a weight of 1+, so loops won't be a problem because weight increases as a path grows. But... if any of your nodes have a cost/weight <= 0, then you should include a maximum time or depth in order to stop pathfinding.

If you want perfect paths, use Djikstra's algorithm. If you don't care about perfection, use A*. When you create a list of candidate nodes, validate them before adding them to the search list. Any nodes that have a higher total weight than your maximum should be removed from the candidate list.

So something like this:

Current node -> List of candidate nodes --(are they less?)-> List of next nodes
merge(list of search nodes, list of next nodes)

Instead of stopping when you find a goal node, add goal nodes to a list and make paths when you finish pathfinding. Most implementations of pathfinding nodes look like this:

Node
- Node previous
- depth, cost
- misc data (coordinates, hp, gold, terrain, entity)

Tracing the path is pretty easy: add the goal node to a list, then add previous to the list until previous = null. The list is your path.

Pathfinding is a very powerful tool, but most of the algorithms and guides you can find online are introductions and even the best guide I found, Amit Patel's A* Tutorial, isn't very deep.

Many pathfinding systems can only find one path because they're too specialized, so I generalized the algorithms. Below, you'll find an in-depth guide to pathfinding that has more information than you can find with google. The reason I include it is because it allows you to derive much more powerful pathfinding algorithms, such as finding multiple paths and goals, beginning from multiple starting locations, or even managing execution time. It will help you implement your algorithm.

In-depth Pathfinding Guide

Everything you need to make new pathfinding systems

The essence of pathfinding is this algorithm:

  1. Start with a listopen of nodes (usually contains 1 item)
  2. Select the most promising1 node
    • If the node is a goal2, add it to a listgoal
    • if the node is valid, generate a list of adjacent3 candidate nodes (listcand)
  3. For each candidate, if it is invalid4, remove it from listcand. Afterwards, add listcand to listopen.
  4. Remove the selected node from listopen and add it to listclosed
  5. Repeat step 2 while pathfinding5

Notes:

[1]: This is where most pathfinding algorithms diverge; they prioritize nodes differently.

  • First In, First Out (oldest) is the simplest algorithm; just check nodes in the order they are added to listopen. Often looks like BFS.
  • First In, Last Out (newest) chooses the newest nodes added to the list. It can look a lot like DFS depending on your node generator.
  • BFS searches the least depth and isn't usually the best algorithm to choose.
  • DFS prioritizes the node with the greatest depth. It tends to choose a path and keep walking down it, even if it goes forever.
  • Greedy chooses the lowest cost/weight to move to. The search can get stuck in a high cost area and end up with a path that has a very high cost compared to the perfect solution. Usually A* is a better compromise between speed and optimality.
  • Dijkstra's chooses a node with the lowest total cost/weight. It is very slow in large areas but if you want perfect solutions, it is a good choice.
  • Best-first chooses the node with the lowest (estimated) remaining cost to reach the goal. In many cases, the estimate is the actual distance to goal (euclidean, manhattan, etc.), but it is not always possible to know.
  • A* is Dijkstra + Best-first. These two heuristics cancel eachother out so that in open areas, A* moves quickly, but doesn't get stuck. A* is not perfect, but it's much faster than Dijkstra's. You can weight either heuristic to make the algorithm more greedy or more optimal, and you can also add other cost functions like distance to health kits, cover, or enemy players.
  • Custom heuristics usually come into play when your nodes contain a lot of data. This usually means that you've moved from pathfinding into the realm of state-space searches like predicting the next move your opponent will make in chess. Problems that involve resource management will use a custom heuristic to prioritize each resource in order to determine the weight of a node.

[2]: Sometimes the goal node isn't a single location. There may be times that you want to find any node that has a certain object like a health kit, a shop, or an enemy player that's easy to kill. By checking the node with a goal() function, it becomes possible to define multiple end-points.

[3]: candidate nodes don't need to be beside eachother. What you are doing is using a function f(node) = list(nodes). When searching a gamestate to get gold or health for a player, you can create nodes for actions like JUMP, ATTACK, REST, etc. In some cases, you'll want to validate the list of generated nodes before you add them.

[4]: Invalid nodes are usually just nodes in listclosed that have been searched before. However, they can be nodes that are too far, nodes that collide with walls (really common), nodes that would reduce your player health to 0, etc. If you decide not to use node isn't in closed list as a condition, then your algorithm is allowed to backtrack (which can create infinite loops).

[5]: You can implement the algorithm to stop when certain conditions are fulfilled. Normally this is assumed to be that 1 goal node is found, but there's so much you can do with this! You can stop pathfinding after a certain amount of time, which is excellent for game engines because you may need to pause to render frames and prevent lag. You can also stop searching if the node list becomes too big, keeping memory usage low. You can even stop once you have a certain amount of solutions.

This boolean stop condition/function allows you to abort pathfinding whenever the pathfinder takes too long, hogs resources or falls into an infinite loop. On a single thread, this usually means you no longer need the pathfinder. For game engines, online game clients and GUI applications, I like to run the pathfinder in a second thread and wake it up whenever I need it. If the pathfinder doesn't find a path fast enough, a dumb AI makes quick decisions until pathfinding finishes.

Emelina answered 3/5, 2016 at 6:57 Comment(2)
A* finds the same perfect paths as Dijkstra's Algorithm as long as the heuristic is consistent and doesn't overestimate. The main difference is that Dijkstra's Algorithm isn't directed towards a goal, so it explores all directions equally, whereas A* explores the nodes closer to the goal more than directions away from the goal. You can think of A* as Dijkstra's but reweighted.Torytoryism
Where is your quoted guide from?Levins

© 2022 - 2024 — McMap. All rights reserved.