Prolog - Difference between two lists BUT - prolog

Using SWI-Prolog's listing predicate (or SICStus' predicate in its list library), we have:
lists:subtract([], _, []) :- !.
lists:subtract([A|C], B, D) :-
memberchk(A, B), !,
subtract(C, B, D).
lists:subtract([A|B], C, [A|D]) :-
subtract(B, C, D).
which does this successfully:
?- subtract([2,3,4,5],[3,4],X).
X = [2, 5].
BUT, what if I want to do:
?- new_subtract([2,3,4,5],[3,X],Y).
X = [3, 2],
X = [3, 4],
X = [3, 5],
Y then has three solutions by taking the three X solutions from [2,3,4,5].
However, subtract/2 doesn't allow for this.
I've been trying to solve this for ages by taking the cuts (!)s out of the built in predicate's body to try and get it to backtrack and find all the solutions.

I assume you mean
?- new_subtract([2,3,4,5],[3,X],Y).
Y = [3, 2] ;
Y = [3, 4] ;
Y = [3, 5]
The following definition does that, but does not preserve all of subtract/3's behavior:
sub(List,[],List).
sub(List,[X|Sub],Rem) :- select(X,List,Rem0), sub(Rem0,Sub,Rem).
Usage:
?- sub([2,3,4,5],[3,X],Y).
X = 2,
Y = [4, 5] ;
X = 4,
Y = [2, 5] ;
X = 5,
Y = [2, 4] ;
false.

Related

Combinations in all orders

I want to generate all pairs of all the numbers of a range 1..N in all orders i.e. [X,Y] and [Y,X]
I found these here : http://kti.ms.mff.cuni.cz/~bartak/prolog/combinatorics.html
comb(0,_,[]).
comb(N,[X|T],[X|Comb]):-N>0,N1 is N-1,comb(N1,T,Comb).
comb(N,[_|T],Comb):-N>0,comb(N,T,Comb).
comb_rep(0,_,[]).
comb_rep(N,[X|T],[X|RComb]):-N>0,N1 is N-1,comb_rep(N1,[X|T],RComb).
comb_rep(N,[_|T],RComb):-N>0,comb_rep(N,T,RComb).
?- findall(E1,comb_rep(2,[1,2,3],E1),C1),findall(E2,comb(2,[3,2,1],E2),C2),append(C1,C2,C),write(C).
[[1,1],[1,2],[1,3],[2,2],[2,3],[3,3],[3,2],[3,1],[2,1]]
There have to be an easier way ?
Also i would prefer to do it as GENERATOR, rather than a FULLY rendered list (like the ex above)
I suspect I am missing something because this does so much less than the code you posted, but this seems to create the pairs how you ask:
limit_pairs(N, [X, Y]) :-
between(1, N, X),
between(1, N, Y).
e.g.
?- limit_pairs(3, P).
P = [1, 1]
P = [1, 2]
P = [1, 3]
P = [2, 1]
P = [2, 2]
P = [2, 3]
P = [3, 1]
P = [3, 2]
P = [3, 3]

List of subset sums Prolog

Given a list L of natural numbers I need to create a list containing the sums of the elements of all the subsets of L. For example if L=[1,3,6] I want to obtain the list [0,1,3,4,6,7,9,10].
I tried to use this code
subsetSums(List,Sums) :- findall(Sum,(subset(Sub,List),sum_list(Sub,Sum)),Sums).
but with the following query I get [0] as the only result instead of [0,1,2,3]
?- subsetSums([1,2],Sums).
Where am I wrong?
EDIT: I'm working on SWI Prolog and subset/2 should be a native predicate.
As suggested in the comment, you have to write your own subset/2 predicate and then use findall/3 on this predicate, like this:
subset([], []).
subset([E|T], [E|T1]):-
subset(T, T1).
subset([_|T], L):-
subset(T, L).
subsetSums(List,Sums) :-
findall(S,(subset(List,Sub),sumlist(Sub,S)),Sums).
?- subsetSums([1,2],L).
L = [3, 1, 2, 0]
?- subsetSums([1,2,3],L).
L = [6, 3, 4, 1, 5, 2, 3, 0]
where the output of subset/2 is:
subset([1,2,3],L).
L = [1, 2, 3]
L = [1, 2]
L = [1, 3]
L = [1]
L = [2, 3]
L = [2]
L = [3]
L = []

Predicate for returning permutations of the order of elements a given list in prolog

I'm trying to work out how this predicate in Prolog is working to produce permutations but I can't figure out the second predicate of sel - and was wondering whether this was clear to anyone else?
% permutation(L1, L2): L2 is a permutation of L1
permutation([], []).
permutation(L1, [X|Y]):-
sel(L1, X, T),
permutation(T, Y).
sel([X|Y], X, Y).
sel([X|Y], Z, [X|T]):-
sel(Y, Z, T).
sel/3 simply find a permutation of the list splitting head and rest of the list. if you call it you get:
?- sel([1,2,3],A,B).
A = 1,
B = [2, 3]
A = 2,
B = [1, 3]
A = 3,
B = [1, 2]
false
Note that, in prolog, predicates are tested in order so if you run this
sel([X|Y], X, Y).
sel([X|Y], Z, [X|T]):-
sel(Y, Z, T).
solve(L,LO):-
findall([A,B],sel(L,A,B),LO).
you get
L = [[1, [2, 3]], [2, [1, 3]], [3, [1, 2]]]
But if you swap the two predicates like this
sel([X|Y], Z, [X|T]):-
sel(Y, Z, T).
sel([X|Y], X, Y).
solve(L,LO):-
findall([A,B],sel(L,A,B),LO).
you get
L = [[3, [1, 2]], [2, [1, 3]], [1, [2, 3]]]
and this obviously will change the result.

Prolog counting with restrictions

I don't know how can I achieve the following:
I want to count the number of times a certain condition (whose values are unknown) is met.
For instance, if I have the lists [A1,A2,A3] and [B1,B2,B3], how can I
create a list [R1,R2,R3] where Ri is 1 if Ai=Bi and 0 if not.
This is the basis of the "program".
:- use_module(library(clpfd)).
main(A,B) :-
length(A,3),
domain(A,1,3),
all_different(A),
length(B,3),
domain(B,1,3),
all_different(B),
append(A,B,L),
labeling([],L).
you should 'reify' your conditions, posting constraints of the form
reify(A,B,C) :-
C #<==> A #= B.
between pairs of variables. maplist/3 it's an handy shortcut
:- use_module(library(clpfd)).
% simulate domain/3 in SWI-prolog
domain(Vs,L,H) :- Vs ins L..H.
reify(A,B,C) :-
C #<==> A #= B.
main(A,B,C) :-
length(A,3),
domain(A,1,3),
all_different(A),
length(B,3),
domain(B,1,3),
all_different(B),
maplist(reify, A,B,C),
labeling([],A),
labeling([],B).
yields
1 ?- main(A,B,C).
A = B, B = [1, 2, 3],
C = [1, 1, 1] ;
A = [1, 2, 3],
B = [1, 3, 2],
C = [1, 0, 0] ;
A = [1, 2, 3],
B = [2, 1, 3],
C = [0, 0, 1]
etc ....

Get list of sets where the sum of each set is X

I'm trying to figure out how to generate a list of sets, where each set has a length of N and the sum of each set is X.
I found this code:
num_split(0,[]).
num_split(N, [X | List]):-
between(1,N,X),
plus(X,Y,N),
num_split(Y,List).
And I can use that to get a list of sets with sum X:
num_split(6,List),length(List,5).
List = [1, 1, 1, 1, 2] ;
List = [1, 1, 1, 2, 1] ;
List = [1, 1, 2, 1, 1] ;
List = [1, 2, 1, 1, 1] ;
List = [2, 1, 1, 1, 1] ;
false.
The problem is that those are all permutations, and I'm looking for combinations. The output I'm looking for should be something like get_combos(Sum,Length,List):
get_combos(6,2,List).
List = [5,1];
List = [4,2];
List = [3,3];
false.
Any pointers?
If you have access to a CLP(FD) library, you can use this code:
:- [library(clpfd)].
get_combos(Sum, Length, List) :-
length(List, Length),
List ins 1 .. Sum,
% all_distinct(List), not really useful here
sum(List, #=, Sum),
chain(List, #<),
label(List).
test:
?- get_combos(10,3,L).
L = [1, 2, 7] ;
L = [1, 3, 6] ;
L = [1, 4, 5] ;
L = [2, 3, 5] ;
Maybe I misunderstood your question. Use this chain
...
chain(List, #=<),
....
to get possible duplicates values:
?- get_combos(10,3,L).
L = [1, 1, 8] ;
L = [1, 2, 7] ;
L = [1, 3, 6] ;
L = [1, 4, 5] ;
L = [2, 2, 6] ;
L = [2, 3, 5] ;
L = [2, 4, 4] ;
L = [3, 3, 4] ;
false.
Enforce an "equal or greater" restriction between successive values in the array.
You can add it on as another predicate:
is_combination([]).
is_combination([_]).
is_combination([A,B|List]) :- A =< B, is_combination([B|List]).
get_combos(Sum, Length, List) :-
num_split(Sum, Length, List),
is_combination(List).
Unfortunately, tacking it on the end of the num_split/3 does not necessarily increase its performance, so adding it directly into the algorithm would be marginally better:
get_combos(_, 0, []).
get_combos(Sum, 1, [Sum]).
get_combos(Sum, Length, [A, B|List]) :-
between(1, Sum, A),
plus(A, NextSum, Sum),
plus(1, NextLength, Length),
get_combos(NextSum, NextLength, [B|List]),
A =< B.
I'm not sure just how much more performance this gets, as the comparison has to be after the recursion, due to the less-than-or-equals operator (=<) requiring both operands to be fully instantiated for it to work.

Resources