I would like to implement a function that finds all possible paths to all possible vertices from the original vertex V in a directed cyclic graph G.
Now performance doesn't matter, I just wanted to understand the algorithm. I read the definition of the depth search algorithm, but I do not have a full understanding of what to do.
I do not have ready-made code to provide here, because I'm not sure how:
- save the results (together with A-> B-> C-> we must also store A-> B and A-> B-> C);
- present a graph (digraph? list of tuples?);
- how many recursions to use (work with each neighboring vertex?).
How to find all possible paths from one given vertex of a source in an oriented cyclic graph in Erlang?
UPD: Based on the answers so far, I need to redefine the definition of the graph: it is a non-acyclic graph. I know that if my recursive function falls into a loop, it is an undefined loop. To avoid this, I can simply check if the current vertex is in the list of the resulting path - if so, I stop moving and returning the path.
UPD2: Thanks for the comment causing the comments! Yes, I need to find all the simple paths that do not have cycles from one source vertex to all the others.
In a graph like this:
with the original vertex A, the algorithm should find the following paths:
- A, B
- A, B, C
- A, B, C, D
- A, d
- A, D, C
- A, D, C, B
The following code does the job, but it is unsuitable for graphs with more than 20 vertices (I think this is wrong with recursion - it takes up too much memory, it never ends):
dfs(Graph,Source) -> ?DBG("Started to traverse graph~n", []), Neighbours = digraph:out_neighbours(Graph,Source), ?DBG("Entering recursion for source vertex ~w~n", [Source]), dfs(Neighbours,[Source],[],Graph,Source), ok. dfs([],Paths,Result,_Graph,Source) -> ?DBG("There are no more neighbours left for vertex ~w~n", [Source]), Result; dfs([Neighbour|Other_neighbours],Paths,Result,Graph,Source) -> ?DBG("///The neighbour to check is ~w, other neighbours are: ~w~n",[Neighbour,Other_neighbours]), ?DBG("***Current result: ~w~n",[Result]), New_result = relax_neighbours(Neighbour,Paths,Result,Graph,Source), dfs(Other_neighbours,Paths,New_result,Graph,Source). relax_neighbours(Neighbour,Paths,Result,Graph,Source) -> case lists:member(Neighbour,Paths) of false -> ?DBG("Found an unvisited neighbour ~w, path is: ~w~n",[Neighbour,Paths]), Neighbours = digraph:out_neighbours(Graph,Neighbour), ?DBG("The neighbours of the unvisited vertex ~w are ~w, path is: ~w~n",[Neighbour,Neighbours,[Neighbour|Paths]]), dfs(Neighbours,[Neighbour|Paths],Result,Graph,Source); true -> [Paths|Result] end.
UPD3:
The problem is that the usual depth search algorithm will first go one of the paths: (A, B, C, D) or (A, D, C, B) and never go the second way.
In any case, this will be the only way - for example, when a regular DFS departs from (A, B, C, D), it returns to A and checks if D is visiting (second neighbor A), and since regular DFS maintains a global state for each tops, D will have a βvisitedβ state.
So, we need to introduce a recursion-dependent state - if we step back from (A, B, C, D) to A, we must have (A, B, C, D) in the list of results and we must have D marked as invisible as at the very beginning of the algorithm.
I tried to optimize the solution with tail recursive, but still the algorithm is not workable - it takes about 4 seconds to go through a tiny graph of 16 vertices with 3 edges to the vertex:
dfs(Graph,Source) -> ?DBG("Started to traverse graph~n", []), Neighbours = digraph:out_neighbours(Graph,Source), ?DBG("Entering recursion for source vertex ~w~n", [Source]), Result = ets:new(resulting_paths, [bag]), Root = Source, dfs(Neighbours,[Source],Result,Graph,Source,[],Root). dfs([],Paths,Result,_Graph,Source,_,_) -> ?DBG("There are no more neighbours left for vertex ~w, paths are ~w, result is ~w~n", [Source,Paths,Result]), Result; dfs([Neighbour|Other_neighbours],Paths,Result,Graph,Source,Recursion_list,Root) -> ?DBG("~w *Current source is ~w~n",[Recursion_list,Source]), ?DBG("~w Checking neighbour _~w_ of _~w_, other neighbours are: ~w~n",[Recursion_list,Neighbour,Source,Other_neighbours]), ? DBG("~w Ready to check for visited: ~w~n",[Recursion_list,Neighbour]), case lists:member(Neighbour,Paths) of false -> ?DBG("~w Found an unvisited neighbour ~w, path is: ~w~n",[Recursion_list,Neighbour,Paths]), New_paths = [Neighbour|Paths], ?DBG("~w Added neighbour to paths: ~w~n",[Recursion_list,New_paths]), ets:insert(Result,{Root,Paths}), Neighbours = digraph:out_neighbours(Graph,Neighbour), ?DBG("~w The neighbours of the unvisited vertex ~w are ~w, path is: ~w, recursion:~n",[Recursion_list,Neighbour,Neighbours,[Neighbour|Paths]]), dfs(Neighbours,New_paths,Result,Graph,Neighbour,[[[]]|Recursion_list],Root); true -> ?DBG("~w The neighbour ~w is: already visited, paths: ~w, backtracking to other neighbours:~n",[Recursion_list,Neighbour,Paths]), ets:insert(Result,{Root,Paths}) end, dfs(Other_neighbours,Paths,Result,Graph,Source,Recursion_list,Root).
Any ideas to run this at an acceptable time?