I'm trying to solve this problem and I've already read this answer, but my problem is with infinte looping even if I've used a visited node list.
Let's see my two tries:
edge(1,2).
edge(1,4).
edge(1,3).
edge(2,3).
edge(2,5).
edge(3,4).
edge(3,5).
edge(4,5).
% ------ simple path finding in a directed graph
% ----- simple exploration
path0(A,B, Result) :-
path0(A, B, [], Result).
path0(A, B, _, [e(A,B)]):-
edge(A,B).
path0(A, B, Visited, [e(A,X)|Path]):-
edge(A, X), dif(X, B),
\+ member(X, Visited),
path0(X, B, [A|Visited], Path ).
%---- 1. exploration and length
path(A, B, _, [e(A,B)], 1):-
edge(A,B).
path(A, B, Visited, [e(A,X)|Path], Length):-
edge(A, X),
\+ member(X, Visited),
length(Path, L), % ERR: Path refers to a open list
Length is L + 1,
path(X, B, [A|Visited], Path, _).
% --- 2. not working
path2(A,B, Result, Length) :-
path2(A, B, [], Result, Length).
path2(A, B, [], [e(A,B)], 1):-
edge(A,B).
path2(A, B, Visited, [e(A,X)|Path], Length):-
edge(A, X), dif(X, B),
\+ member(X, Visited),
path2(X, B, [A|Visited], Path, Len),
Length is Len + 1.
Which give me similar answers, i.e.:
?- path(1,3, Path, Length).
Path = [e(1, 3)],
Length = 1 ;
Path = [e(1, 2), e(2, 3)],
Length = 2 ;
And then the Swi-Prolog IDE freezes.
I would like to get rid of the length/2 use. Thanks.
Edit:
So, I figured out this should be the cleaner way of doing it, even if I would like something more similar to the second implementation which would be easier to transform in a shortest path problem solver, since it would be just a min{ pathLengths } from the first call of path3/4.
% ---- 3. working
%
min(A,B,A):- A =< B, !. % for future use (shortest path)
min(_,B,B).
path3(From, To, Path, Len):-
path0(From, To, [], Path),
length(Path, Len).
%min(Len, MinLength, ?)
And this is the corrected version of the second implementation path2:
% --- 2.
% errors: 1. in base case I have to return Visited trough _,
% I can't pass a void list []
% 2. dif(X,B) is unuseful since base case it's the first clause
path2(A,B, Result, Length) :-
path2(A, B, [], Result, Length).
path2(A, B, _, [e(A,B)], 1):- % If an edge is found
edge(A,B).
path2(A, B, Visited, [e(A,X)|Path], Length):-
edge(A, X),
%tab(1),write(A),write('-'),write(X),
\+ member(X, Visited),
%tab(1),write([A|Visited]),write(' visited'),nl,
path2(X, B, [A|Visited], Path, Len),
Length is Len + 1.
The reason why both path/4
and path2/4
expose similar non-termination behavior is because both use the very same auxiliary predicate path/5
. You probably meant path2/5
instead:
path2(A,B, Result, Length) :-
path(A, B, [], Result, Length).
% ^^^^ replace by path2
Maybe first, let's look why your path/4
definition loops. To see this, I will insert goals false
into your program. These goals will reduce the number of inferences. When the remaining fragment still loops, we can be sure that we see a part that is responsible for non-termination. After some experiments, I found the following fragment, called a failure-slice:
edge(1,2).edge(1,4) :- false.edge(1,3) :- false.edge(2,3) :- false.edge(2,5) :- false.edge(3,4) :- false.edge(3,5) :- false.edge(4,5) :- false. path(A,B, Result, Length) :- path(A, B, [], Result, Length), false.path(A, B, _, [e(A,B)], 1):- false,edge(A,B). path(A, B, Visited, [e(A,X)|Path], Length):- edge(A, X), \+ member(X, Visited), length(Path, L), false,Length is L + 1,path(X, B, [A|Visited], Path, _).
So essentially it's the use of the length/2
predicate. As long as the length of the path is not fixed, this fragment will not terminate. So for the query
?- path(1, 3, Path, N).
The Path
is not limited in its length and thus length/2
will find infinitely many solutions - and thus will not terminate.
But, after all, why do you want to know the length anyway? The path argument describes it already implicitly.
For your definition path/4,5
consider what the query
?- path(1, X, Path, N).
should produce as an answer. Should Path = [1]
be a solution, too? It's a bit a question of the exact definition of a path/walk. I think it should.
For a generic solution, please refer to this answer. With it, you can define the predicates that you are interested in like so:
yourpath(A,B, Path, N) :-
path(edge, Path, A,B),
length(Path, N).
But, I would rather not add the extra argument about the length of the path. You can add that information later anytime anyway.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With