Finding distance between nodes of a graph in prolog - prolog

I have a graph in Prolog represented by the edges & weights:
connected(a,b,2).
connected(b,e,1).
connected(b,l,5).
connected(b,g,2).
connected(c,s,2).
connected(d,a,2).
connected(d,k,4).
connected(d,l,7).
connected(e,m,2).
I need to write a predicate which takes a list of nodes & distance.
?- dist([a,b,e],X).
X=3
I've tried to write it, but it is very clumsy & doesn't give the intended result.
The basic idea I have is:
If it is a list of 2 elements then see if they are connected.
If more then 2 elements in the list: see if the 1st element & 2nd element are connected,
recursively see if the next elements are connected.
I have defined 2 auxiliary predicates for head & tail.
dist([A, B], X) :-
connected(A, B, X).
dist([A|B], Length) :-
connected(A, hd(B,H,N), X), % sees if A & next element in the list are connected
dist(tl(B,H,N), Length1), % recursive call with the list excluding element A
Length is X + Length1.
hd([H|T],H,Q).
tl([H|T],T,Q).
I am very new to Prolog land and I am still trying to comprehend the language semantics.
Please suggest an efficient way to go about this problem.

dist([_], 0). % path of length 0 has distance 0
dist([A, B | T], L) :-
connected(A, B, L1), % A and B are connected directly, the distance is L1
dist([B|T], L2), % the distance between B and the last element is L2
L is L1 + L2.

Related

Verify if an element is present in a matrix in ProLog

Good night everyone,
I'm in the middle of some busy days trying to deliver a small project for my Logic Programming classes, where the theme is based on operations on matrices.
Well, one of those asked operations was to verify if a certain element exists in a given matrix, and I'm having some issues with it as I usually have my mind set for imperative programming.
For now, i reached this:
isElemPresent(_, []) :- !.
isElemPresent(Elem, [M|Mt]) :- isElemPresent(Elem,Mt) ; isElemPresentRow(Elem,M).
isElemPresentRow(Elem, [Elem|_]).
isElemPresentRow(Elem, [_|T]) :- isElemPresentRow(Elem, T).
I would really appreciate if someone could guide to my goal, and if possible tell what lacks on my code.
% The element belongs to the list if it matches
% the head of the list
isElemPresent(X,[X|_]).
% The element belongs to the list if it is
% in the head (another array) of the list.
isElemPresent(X,[A|_]):- isElemPresent(X,A).
% The element belongs to the list if it is
% in the queue of the list.
isElemPresent(X,[_|R]):- isElemPresent(X,R).
For instance:
?- isElemPresent(4,[[1,2,5],[6,3,4]]).
Yes
is_elem_present(Elem, Matrix) :-
member(Row, Matrix),
member(Elem, Row).
?- is_elem_present(Elem, [[1,2,5],[6,3,4]]).
Elem = 1 ;
Elem = 2 ;
Elem = 5 ;
Elem = 6 ;
Elem = 3 ;
Elem = 4.
?- is_elem_present(2, [[1,2,5],[6,3,4]]).
true ;
false.
And here's how to do it with indexes:
is_elem_present(I, J, Elem, Matrix) :-
nth0(I, Matrix, Row),
nth0(J, Row, Elem).
?- forall(is_elem_present(I,J,Elem,[[1,2,5],[6,3,4]]), writeln([I,J]=Elem)).
[0,0]=1
[0,1]=2
[0,2]=5
[1,0]=6
[1,1]=3
[1,2]=4
true.

Split a list multiple time in Prolog

this my code :
div2(L,N,L1,L2) :-
length(L1,N),%n=4
append(L1,L2, L),
L=L2,L1=[],L2=[].
i want it to display each time in L1 a 4 element list but it return false.
example :
L=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
L1=[1,2,3,4]
L1=[5,6,7,8]
L1=[9,10,11,12]
L1=[13,14,15,16]
how can i make it work. and thanks for the help.
Assuming you want div(L,N,L1,L2) to be that L1 is the first N element of L, and L2 is what remains:
div2(L,N,L1,L2) :-
length(L1,N),
append(L1,L2,L).
There are no other conditions that need to be placed on L, L1 or L2.
If you want to be the first 4 elements after some multiple of 4 elements, and L2 to be the rest, you need to say so:
div2(L,N,L1,L2) :-
append(L01,L2,L), % break up L
length(L01,Nx), % make sure prefix's length is multiple of N
0 is mod(Nx,N),
append(L0,L1,L01),% set L1 to be last N elements of prefix
length(L1,N).
A slightly different solution...
div2([X|Xs], N, P) :-
( length(P1, N),
append(P1, R, [X|Xs])
-> ( P = P1
; div2(R, N, P)
)
; P = [X|Xs]
).
This solution defines a list P1 of length N, and attempts to unify it (with append as a prefix list of [X|Xs]. ([X|Xs] is used instead of L to ensure that the predicate succeeds only if the first argument is a list with at least one element.)
If the append is successful, then the solution is either P1 (P is unified with P1) or a recursive call to div2 with the remainder of the first argument list with the prefix P1 absent.
If the append fails (which will happen if the number of partition elements N is larger than the first argument list length), then P is unified with the first argument list.

List less in Prolog

I want to write a predicate for 2 lists list_less(L1, L2) that is true if the list L1 is less than the list L2 with respect to the ordering below:
list_less(L1, L2) iff
Let L1' := complement(L1,L2), L2' := complement(L2,L1)
(complement(L1,L2) contains those elements of L1
that are not in L2)
Let m1 := max(L1'), m2 := max(L2')
(max(L) gives the maximal element of L with respect to
the standard order #<)
m1 #< m2.
and the output something like this :
?- list_less([3,3,3,3,2,2],[3,3,4,0]).
true.
?- list_less([a,b,X,Y,[X|Y],2], [[X,X|Y]]).
true.
?- list_less([a,b,X,Y,[X|Y],2], [X,b,b]).
false.
I started with this :
list_less([],[]).
list_less([H|T],[X|Y]):-
complement([H|T],[X|Y],L),
max_list(H|T],M1).
complement([],[],[]).
complement([H|T],[X|Y],L):-
member(H,[X|Y]),
!,
complement(T,Y,[X|_]).
max_list(L, Max):-
select(Max, L, Rest),
\+ (member(E, Rest), E > Max).
Here is a more compact solution (than one that computes the difference lists first).
list_less(As, Bs):-
sort(As, SortedAs),
reverse(SortedAs, RevAs),
sort(Bs, SortedBs),
reverse(SortedBs, RevBs),
RevAs #< RevBs.
The two lists are first sorted and reversed and then compared w.r.t. the standard order of terms. That means that elements are being compared starting from the left. Hence, the first two different elements (that correspond to the maximum element of each list that doesn't occur in the other) will make the comparison succeed or fail. This works because sort/2 removes the duplicates also.
| ?- list_less([3,3,3,3,2,2],[3,3,4,0]).
(4 ms) yes
| ?- list_less([a,b,X,Y,[X|Y],2], [[X,X|Y]]).
yes
| ?- list_less([a,b,X,Y,[X|Y],2], [X,b,b]).
no

Longest increasing subset Prolog

I want to create in Prolog to find longest increasing subset of entered list. For example, you enter list of [3,1,2] and the output is [1,2],
?- subset([3,1,2], X).
X = [1,2]
I have code which shows all the subsets of this list:
subset([],[]).
subset([_|X],Y):-subset(X,Y).
subset([A|X],[A|Y]):-subset(X,Y).
Can anyone help me to find just the longest increasing subset?
Do you mean [1,3,5,6,7] to be the answer for [4,1,3,8,9,5,6,7]? IOW, do you really mean subsets, or just sublists, i.e. contiguous portions of the list?
If the latter is the case, you won't need subsets. The search is linear. If in a list [a,b,c,d,e,f] you find that d > e and the increasing sequence [a,b,c,d] stops, you don't need to restart the search from b now: the sequence will still break at d. You will just continue your search from e.
So, we'll just carry around some additional information during the search, viz. the current and the winning-so-far sub-sequences. And their lengths.
longest_incr([],0-[]).
longest_incr([A|B],RL-R):- % R is the result, of length RL
longest_aux(B,[],0, [A],1, RL-R).
longest_aux([], Win,N, Curr,K, RL-R):-
( K>N -> RL=K, reverse(Curr,R) ; RL=N, reverse(Win,R) ).
longest_aux([A|B],Win,N, Curr,K, RL-R):- Curr = [X|_], L is K,
( A>X -> longest_aux(B,Win, N, [A|Curr],L+1,RL-R) % keep adding
; L>N -> longest_aux(B,Curr,K, [A], 1, RL-R) % switch the winner
; longest_aux(B,Win, N, [A], 1, RL-R) % winner unbeaten
).
If OTOH you really need the longest subset ... there's a contradiction there. A set can have its elements rearranged, so the longest subset of a given list will be
longset_subset(L,R):- sort(L,S), R=S.
Perhaps you mean the longest order-preserving sub-sequence, i.e. it is allowed to be non-contiguous. Then you can gather all solutions to your subset with findall or similar predicate, and analyze these results:
longest_subseq(L,R):-
findall( S, subset(L,S), X),
maplist( longest_incr, X, Y),
keysort( Y, Z),
last( Z, _Len-R).
The above has a lot of redundancy in it. We can attempt to improve its efficiency by only allowing the increasing subsequences:
incr_subseq([],[]).
incr_subseq([_|X],Y):- incr_subseq(X,Y).
incr_subseq([A|X],[A|Y]):- incr_subseq(X,Y), ( Y=[] ; Y=[B|_], A<B).
Now all the sub-sequences found by the above predicate will be increasing, so we can just take their lengths:
lenlist(List,Len-List) :- length(List,Len).
longest_subseq(L,R):-
findall( S, incr_subseq(L,S), X),
maplist( lenlist, X, Y),
keysort( Y, Z),
last( Z, _Len-R).
Or, the linear searching longest_incr could be tweaked for a more efficient solution. Instead of maintaining just one winning sub-sequence, it would maintain all the relevant possibilities as it goes along the input list.
Just out of curiosity, would it be possible in prolog to realize something like this for finding longest increasing subsequence:
You find all subsets of list
Than you find, which of these subsets are increasing
And then you search for the longest
If it's possible, how could I do that in Prolog?

Prolog - sequence in list

We want to build a predicate that gets a list L and a number N and is true if N is the length of the longest sequence of list L.
For example:
?- ls([1,2,2,4,4,4,2,3,2],3).
true.
?- ls([1,2,3,2,3,2,1,7,8],3).
false.
For this I built -
head([X|S],X). % head of the list
ls([H|T],N) :- head(T,X),H=X, NN is N-1 , ls(T,NN) . % if the head equal to his following
ls(_,0) :- !. % get seq in length N
ls([H|T],N) :- head(T,X) , not(H=X) ,ls(T,N). % if the head doesn't equal to his following
The concept is simply - check if the head equal to his following , if so , continue with the tail and decrement the N .
I checked my code and it works well (ignore cases which N = 1) -
ls([1,2,2,4,4,4,2,3,2],3).
true ;
false .
But the true answer isn't finite and there is more answer after that , how could I make it to return finite answer ?
Prolog-wise, you have a few problems. One is that your predicate only works when both arguments are instantiated, which is disappointing to Prolog. Another is your style—head/2 doesn't really add anything over [H|T]. I also think this algorithm is fundamentally flawed. I don't think you can be sure that no sequence of longer length exists in the tail of the list without retaining an unchanged copy of the guessed length. In other words, the second thing #Zakum points out, I don't think there will be a simple solution for it.
This is how I would have approached the problem. First a helper predicate for getting the maximum of two values:
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- Y > X.
Now most of the work sequence_length/2 does is delegated to a loop, except for the base case of the empty list:
sequence_length([], 0).
sequence_length([X|Xs], Length) :-
once(sequence_length_loop(X, Xs, 1, Length)).
The call to once/1 ensures we only get one answer. This will prevent the predicate from usefully generating lists with sequences while also making the predicate deterministic, which is something you desired. (It has the same effect as a nicely placed cut).
Loop's base case: copy the accumulator to the output parameter:
sequence_length_loop(_, [], Length, Length).
Inductive case #1: we have another copy of the same value. Increment the accumulator and recur.
sequence_length_loop(X, [X|Xs], Acc, Length) :-
succ(Acc, Acc1),
sequence_length_loop(X, Xs, Acc1, Length).
Inductive case #2: we have a different value. Calculate the sequence length of the remainder of the list; if it is larger than our accumulator, use that; otherwise, use the accumulator.
sequence_length_loop(X, [Y|Xs], Acc, Length) :-
X \= Y,
sequence_length([Y|Xs], LengthRemaining),
max(Acc, LengthRemaining, Length).
This is how I would approach this problem. I don't know if it will be useful for you or not, but I hope you can glean something from it.
How about adding a break to the last rule?
head([X|S],X). % head of the list
ls([H|T],N) :- head(T,X),H=X, NN is N-1 , ls(T,NN) . % if the head equal to his following
ls(_,0) :- !. % get seq in length N
ls([H|T],N) :- head(T,X) , not(H=X) ,ls(T,N),!. % if the head doesn't equal to his following
Works for me, though I'm no Prolog expert.
//EDIT: btw. try
14 ?- ls([1,2,2,4,4,4,2,3,2],2).
true ;
false.
Looks false to me, there is no check whether N is the longest sequence. Or did I get the requirements wrong?
Your code is checking if there is in list at least a sequence of elements of specified length. You need more arguments to keep the state of the search while visiting the list:
ls([E|Es], L) :- ls(E, 1, Es, L).
ls(X, N, [Y|Ys], L) :-
( X = Y
-> M is N+1,
ls(X, M, Ys, L)
; ls(Y, 1, Ys, M),
( M > N -> L = M ; L = N )
).
ls(_, N, [], N).

Resources