Finding all possible paths in a graph without cycle - prolog

I'm trying to write a Prolog program to give me all possible paths between two points in a graph (with cycle).
edge(a,b).
edge(a,c).
edge(a,d).
edge(b,e).
edge(c,e).
edge(c,f).
edge(d,f).
edge(f,g).
edge(g,e).
edge(e,a).
show_path(X,Y,[X,Y]) :- edge(X,Y).
show_path(X,Z,[X|T]) :- edge(X,Y), not(member(Y, T)), show_path(Y,Z,T).
I'm trying to use not(member()) to exclude the cycles and avoid infinite loop but it doesn't yield all possible solutions. How can I alter the program to get the all possible paths between two points in a graph with cycle?

Your program does not work because not(member(Y, T)) will always be false: at this point, T is not instantiated so it's always possible to find a list which contains Y.
You can fix your program by adding an accumulator:
show_path(X,X,T,P) :- reverse([X|T],P).
show_path(X,Z,T,P) :- edge(X,Y), not(member(X,T)), show_path(Y,Z,[X|T],P).
show_path(X,Y,P) :- show_path(X,Y,[],P).
It's not clear what you mean by avoiding cycles. Here, it will avoid passing twice on the same point, unlike #coder's answer. For example:
?- show_path(a,e,Z).
Z = [a, b, e] ;
Z = [a, c, e] ;
Z = [a, c, f, g, e] ;
Z = [a, d, f, g, e] ;
false.

You can easily see that not(member(Y, T)) fails when T is not instantiated. For example try:
?- not(member(X,L)).
false.
where you see that it fails. To solve that you need to keep an extra list that will be instantiated in every step beginning with empty list:
show_path(X,Y,R):-show_path(X,Y,[],R).
show_path(X,Y,_,[X,Y]) :- edge(X,Y).
show_path(X,Y,L,[X|R]) :- edge(X,Z),\+member(Z,L),
show_path(Z,Y,[Z|L],R).
Example:
?- show_path(a,e,L).
L = [a, b, e] ;
L = [a, b, e, a, c, e] ;
L = [a, b, e, a, c, f, g, e] ;
L = [a, b, e, a, d, f, g, e] ;
L = [a, c, e] ;
L = [a, c, e, a, b, e] ;
L = [a, c, e, a, d, f, g, e] ;
L = [a, c, f, g, e] ;
L = [a, c, f, g, e, a, b, e] ;
L = [a, d, f, g, e] ;
L = [a, d, f, g, e, a, b, e] ;
L = [a, d, f, g, e, a, c, e] ;
false.
You could have the output that #Fatalize suggested also by writing:
show_path(X,Y,[X,Y]) :- edge(X,Y).
show_path(X,Y,R) :- edge(X,Z), show_path(Z,Y,RZ),R=[X|RZ],
sort(R,R1),length(R,N),length(R1,N1),
(N>N1->!,fail ;true).
Example:
?- show_path(a,e,L).
L = [a, b, e] ;
L = [a, c, e] ;
L = [a, c, f, g, e] ;
L = [a, d, f, g, e] ;
false.

Related

Prolog: Determine answers to Multiple Choice Question test where students' answers and grades are known

This is a prolog problem that I have to solve. I can't seem to find a starting point.
In a MCQ test where:
each question has 4 choices [a,b,c,d]
each question has only one correct answer (choice)
there are 10 questions
all questions have the same grade value (1 point, totalling 10 points)
4 students have taken this test and we have their grades:
student1: [b, c, b, a, c, c, c, d, c, c] Grade: 7/10
student2: [b, d, c, a, d, d, c, c, a, b] Grade: 6/10
student3: [d, a, b, b, d, d, c, d, a, b] Grade: 5/10
student4: [c, d, c, b, d, b, b, c, a, a] Grade: 3/10
From the informations above I need to write a prolog script that can determine the set of questions that are correct to get a 10/10 grade
We can branch over the possible choices, and do bookkeeping on the score of the students. When we reach the end of the list, then the users need to have the correct score.
We thus can generate lists of choices with:
option(a).
option(b).
option(c).
option(d).
sequence(N, L) :-
length(L, N),
maplist(option, L).
For example for a sequence of two items, we get:
?- sequence(2, L).
L = [a, a] ;
L = [a, b] ;
L = [a, c] ;
L = [a, d] ;
L = [b, a] ;
L = [b, b] ;
L = [b, c] ;
L = [b, d] ;
L = [c, a] ;
L = [c, b] ;
L = [c, c] ;
L = [c, d] ;
L = [d, a] ;
L = [d, b] ;
L = [d, c] ;
L = [d, d].
Next we can make a predicate mark/3 that calculates the score given the hypothetical correct sequence, and the sequence of a student. We thus need to implement something like:
mark([], [], 0).
mark(…, …, …) :-
….
I leave the implementation of mark/3 as an exercise.
Then we thus can find the sequence of correct answers with:
correct(C) :-
sequence(10, C),
mark(C, [b, c, b, a, c, c, c, d, c, c], 7),
mark(C, [b, d, c, a, d, d, c, c, a, b], 6),
mark(C, [d, a, b, b, d, d, c, d, a, b], 5),
mark(C, [c, d, c, b, d, b, b, c, a, a], 3).
You can later optimize the approach to an interleaved generate-and-test and not first generating sequences and then testing these. But I would first start with a simple solution that works.
When I implement this myself, there is exactly one solution. That solution has b as first answer.
You can use library(clfd) and reification, everything is here : https://www.swi-prolog.org/pldoc/man?section=clpfd-reification-predicates
(as already explain on another forum !)

Prolog: Remove circle from graph-like problem

I have the following prolog program where I want to go from position a to position d. We can do that by following the path: a->b->c->d. Another path is: a->b->c->b->c->d etc. How do we remove that 'circling' path? I tried to remove it by using 'not(member(from_to(X,_),Z))' but it doesn't seem to work.
from_to(a, b).
from_to(b, c).
from_to(c, d).
from_to(d, c).
from_to(c, b).
move(X,Y,Z) :- from_to(X,Y), X \= Y,
Z = [from_to(X,Y)].
move(X,Y,Z) :- from_to(X,K), K \= Y, move(K,Y,Z1),
Z = [from_to(X,K)|Z1],
not(member(from_to(X,_),Z)).
(if you remove the line 'not(member(from_to(X,_),Z))' the program works fine but outputs the circling paths)
It is better to use an accumulator here: a variable that you update through recursive calls, and thus contains some sort of "memory". Here the accumulator can store a list of nodes that we have visited. In order to move to a new node, that node should not be in the list.
So we define a predicate move/4 instead of move/3, with:
move(X,Y,Z) :-
move(X, Y, Z, [X]).
Now we can define the predicate move(S, D, Path, Visited) by using two rules:
in case S and D are the same, we are done, regardless what Visited is, we unify Path with [D]; and
otherwise we "walk" to another node N through the from_to/2 predicate, ensure that it is not a member of Visited, then we make a recursive call where we prepend S to the N to the visited nodes. We prepend X to the result of the recursive Z.
Like for example:
move(S, S, [S], _).
move(S, D, [S|Z], Visited) :-
from_to(S, N),
\+ member(N, Visited),
move(N, D, Z, [N|Visited]).
For your sample graph:
it generates then:
?- move(a, d, Z).
Z = [a, b, c, d] ;
false.
?- move(a, D, Z).
D = a,
Z = [a] ;
D = b,
Z = [a, b] ;
D = c,
Z = [a, b, c] ;
D = d,
Z = [a, b, c, d] ;
false.
?- move(A, d, Z).
A = d,
Z = [d] ;
A = a,
Z = [a, b, c, d] ;
A = b,
Z = [b, c, d] ;
A = c,
Z = [c, d] ;
false.
?- move(A, D, Z).
A = D,
Z = [D] ;
A = a,
D = b,
Z = [a, b] ;
A = a,
D = c,
Z = [a, b, c] ;
A = a,
D = d,
Z = [a, b, c, d] ;
A = b,
D = c,
Z = [b, c] ;
A = b,
D = d,
Z = [b, c, d] ;
A = c,
D = d,
Z = [c, d] ;
A = d,
D = c,
Z = [d, c] ;
A = d,
D = b,
Z = [d, c, b] ;
A = c,
D = b,
Z = [c, b] ;
false.
In case a node is not "connected to itself" as in that we have not a path from a to a for example, we can implement move as:
move(S, D, [S|Z], V) :-
from_to(S, N),
\+ member(N, V),
move2(N, D, Z, [N|V]).
move2(S, S, [S], _).
move2(N, D, [S|Z], V) :-
move(N, D, Z, V).

Prolog creating a list of sets from ith elements of lists

I have list structure
L=[[a,b,c,d],[a,f,c,h]]
Length of L can be greater than 2.I want to unite the elements of list so that L or a NewL become
L=[a,[b,f],c,[d-h]]
This is probably what you want:
unite([[],[]], []).
unite([[X|Ls], [X|Rs]], [X|Rest]) :- unite([Ls, Rs], Rest).
unite([[L|Ls], [R|Rs]], [[L,R]|Rest]) :- L \= R, unite([Ls, Rs], Rest).
However, I agree with #false because this is a strange API and there are a lot of unhandled edge cases.
What you're requiring is an aggregation schema. I think I got it:
unite(Ls, [E|Es]) :-
aggreg(Ls, E, Ns),
unite(Ns, Es).
unite(_, []).
aggreg(L, E, LLs) :-
maplist(first, L, Fs, LLs),
setof(X, member(X, Fs), S),
( [E] = S -> true ; E = S ).
first([E|Es], E, Es).
yields
?- L=[[a,b,c,d],[a,f,c,h],[a,f,c,g]],unite(L,U).
L = [[a, b, c, d], [a, f, c, h], [a, f, c, g]],
U = [a, [b, f], c, [d, g, h]] ;
L = [[a, b, c, d], [a, f, c, h], [a, f, c, g]],
U = [a, [b, f], c] .
I think that a cut after the first solution would be well placed (use once/1 for that).
Note that the schema it's rather general: just substitute in setof/3 some more applicative task (if any) than unification (you could call into your DB, for instance).

using prolog to generate sentences

Consider the following list of states:
[Sin,S2,S3,...,Sout]
and following rules:
it is possible to go back from S(n) to S(n-1) if there is such
S(n-1)
it is not possible to go back from S(out)
a sentence always begins with S(in) and ends with S(out)
I would like to have a rule that could be activated like this:
?- sentence(X, backs)
in which 'backs' means how many times a "back" is allowed.
For this list [a,b,c,d]
?- sentence(x, 2)
would generate:
[a,b,c,d] %no backs
[a,b,a,b,c,d] %one back
[a,b,c,b,c,d] %from d we cannot go back
[a,b,a,b,c,b,c,d] %two backs
[a,b,c,b,a,b,c,d] %two backs
Here's something that seems to be working:
sentence( [A|B], N, [A|X]) :- B=[_|_] -> sentence(B,[A],N,X)
; B = X.
sentence( B, _, 0, B). % no more moves back left
sentence( [B,C], _, N, [B,C]):- N>0. % no going back from end node
sentence( [B|C], A, N, [B|X]):- N>0, C=[_|_],
sentence( C, [B|A], N, X). $ fore
sentence( [B|C], [A|D], N, [B|X]):- N>0, C=[_|_], N1 is N-1,
sentence( [A,B|C], D, N1, X). $ aft
Running it gives me
23 ?- sentence([a,b,c,d],2,X).
X = [a, b, c, d] ;
X = [a, b, c, b, c, d] ;
X = [a, b, c, b, c, b, c, d] ;
X = [a, b, c, b, a, b, c, d] ;
X = [a, b, a, b, c, d] ;
X = [a, b, a, b, c, b, c, d] ;
X = [a, b, a, b, a, b, c, d] ;
No

Prolog mystery(c,[a,b,c,d],Z)

I think the answer is 3 but I am not sure, can anyone provide some help?
Suppose the following two statements are entered into Prolog:
mystery(X,[X|L],L).
mystery(X,[Y|L],[Y|M]) :- mystery(X,L,M).
What would Prolog return if one then gives it the following goal?
?- mystery(c,[a,b,c,d],Z).
So, mystery/3 is defined as:
mystery(X, [X|L], L).
mystery(X, [Y|L], [Y|M]) :- mystery(X, L, M).
There are (at least) three ways to look at mystery:
It takes an element X (first parameter), looks for its existence in a given list (second parameter) and returns that same list, minus one occurrence of X (third parameter). Thus:
?- mystery(c, [a, b, c, d], Z).
Z = [a, b, d] ;
fail.
?- mystery(c, [a, b, c, d, c], Z).
Z = [a, b, d, c] ;
Z = [a, b, c, d] ;
fail.
Another way to look at mystery is that it checks whether the lists constituting its second and third argument only differ with respect to one element, i.e. that the second list equals the third list, except that it has one additional element in one place. Thus:
?- mystery(X, [a, b, c, d], [a, b]).
fail.
?- mystery(X, [a, b, c, d], [a, b, c]).
X = d ;
fail.
Note that order is important:
?- mystery(X, [a, b, c, d], [a, c, b]).
fail.
Lastly, mystery can also generate all ways in which the first argument can be interspersed in the list of the third argument. Thus:
?- mystery(d, Y, [a, b, c]).
Y = [d, a, b, c] ;
Y = [a, d, b, c] ;
Y = [a, b, d, c] ;
Y = [a, b, c, d] ;
fail.

Resources