Generate a list of different values with CLPFD - prolog

I am trying to generate all possible combinations of lists of three elements where all are distinct. I am using the CLPFD library to define the domain of the variable.
I defined the following
listDif(F,X):-F ins 1..3,findall(F,all_distinct(F),X).
And the answers to the queries are
?- listDif([1,_,2],X).
X = [[1, 3, 2]].
?- listDif([1,_,_],X).
X = [[1, _7374, _7380]],
_7374 in 2..3,
all_distinct([1, _7374, _7380]),
_7380 in 2..3.
?-
How do I display the lists with the possible integer values?

If you want to generate lists with CLPFD then you need to use lists. :) Your code is just using individual integers.
list3(F) :-
length(F, 3), % F is a list of length 3
F ins 1..3, % Elements of F are in the range 1..3
all_distinct(F). % F has distinct elements
Now you have a predicate which succeeds for unique lists consisting of 1, 2, 3:
?- list3(F), label(F).
F = [1, 2, 3] ;
F = [1, 3, 2] ;
F = [2, 1, 3] ;
F = [2, 3, 1] ;
F = [3, 1, 2] ;
F = [3, 2, 1].
Then you can use findall/3 if you want to have a list of all of these lists:
?- findall(F, (list3(F), label(F)), AllList3).
AllList3 = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]].

Related

How to create a list without using findall? Prolog

I am generating permutations:
takeout(X,[X|T],T).
takeout(X,[F|R],[F|S]):-
takeout(X,R,S).
perm([],[]).
perm([X|Y],Z):-
perm(Y,W),
takeout(X,Z,W).
I want to know how to create a list of all the permutations without using findall.
Example:
?-perm([1,2,3],List).
List = [[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]
Group permutations by the element it starts with.
Take an element X and create permutations Ys1 without it in the original list.
Adding this element X as the first element of all these permutations we have the list XP of permutations starting with X.
Appending all the groups will give you all permutations.
cons(X, Xs, [X|Xs]).
perm([], [[]]).
perm(Xs, Ys) :-
dif(Xs, []),
maplist({Xs}/[X, XP]>>(select(X, Xs, Xs1),
perm(Xs1, Ys1),
maplist(cons(X), Ys1, XP)),
Xs, Yss),
append(Yss, Ys).
?- perm([1, 2, 3], X).
X = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] ;
false.
?- length(Y, 8), perm(Y, X), length(X, N). %8 factorial
N = 40320
The idea is to generate permutations and test if you already created this permutation. I'm using the inbuild predicate permutation/2.
perm(Ori,Out):-
perm(Ori,[],Out).
perm(Ori,Acc,Ret):-
permutation(Ori,Perm),
\+ member(Perm,Acc),
!,
perm(Ori,[Perm|Acc],Ret).
perm(_,L,L).
?- perm([1,2,3],E).
E = [[3, 2, 1], [3, 1, 2], [2, 3, 1], [2, 1, 3], [1, 3, 2], [1, 2, 3]].
The code is not the fastest one since it checks multiple times for membership.

Find all lists of sublists that when concatenated give a given list

I wrote this function that given as a first parameter a list of lists, it generates in the second parameter the result of concatenating all the lists.
appall([],[]).
appall([H|T],V) :- appall(T,V1), append(H,V1,V).
However, I want it to work the other way around - appall(X,[1,2,3]) - to give me X = [[],[1,2,3]] then X=[[1],[2,3]] and so on. This doesn't work because the call appall(T, V1) doesn't decrease.
How do I fix it?
Here is one solution:
split([],[]).
split([Head|Tail],[[Head]|Split]) :-
split(Tail,Split).
split([Head|Tail],[[Head|List]|Split]) :-
split(Tail,[List|Split]).
For example:
?- split([1,2,3,4],Lists), split(Recover,Lists).
Lists = [[1], [2], [3], [4]],
Recover = [1, 2, 3, 4] ;
Lists = [[1], [2], [3, 4]],
Recover = [1, 2, 3, 4] ;
Lists = [[1], [2, 3], [4]],
Recover = [1, 2, 3, 4] ;
Lists = [[1], [2, 3, 4]],
Recover = [1, 2, 3, 4] ;
Lists = [[1, 2], [3], [4]],
Recover = [1, 2, 3, 4] ;
Lists = [[1, 2], [3, 4]],
Recover = [1, 2, 3, 4] ;
Lists = [[1, 2, 3], [4]],
Recover = [1, 2, 3, 4] ;
Lists = [[1, 2, 3, 4]],
Recover = [1, 2, 3, 4] ;
false.
This solution is based on the following observation. I will refer to the flattened list as the input list and the unflattened list as the output list. In the recursive case, the input list has the form [H|T] and split(T,R) succeeds by assumption. There are three cases to consider.
If R = [] we can begin constructing a new list whose last element is H.
If R = [_|_] we can begin constructing a new list whose last element is H.
If R = [L|_] we can continue constructing L by prepending H to L.
In each case, we obtain valid output lists. The first two cases are implemented by the second clause of split/2 (it doesn't matter whether R = [] or R = [_|_]) and the third by the third clause.

Write a program in prolog that determines if there are exactly three values in such a way that they add upto a sum of N

Eg:
List[1,2,3,4,5,6] with N equal to 6 should print true because there are exactly 3 values that add upto 6. 1+2+3.
List[2,5,7,9] with N equal to 12 should print false as there are no 3 elements that add upto 12.
Let's maybe start with a more general predicate that describes the relation between a list, a sublist of said list and the sum of numbers in the sublist. To make it obvious which argument is what, it is opportune to chose a descriptive name for the predicate, say sum_ofsub_fromlist/3. Now let's observe that if the first argument is the sum of the numbers in the sublist, then successively subtracting those numbers from the sum yields zero, e.g.: X=A+B → X-A-B=0. So there will be a base case that contains 0 as the sum and [] as the sublist (rule 1) and a recursive rule that subtracts the elements of the sublist from the sum (rule 2). And since a sublist does not contain all elements of the list it's taken from in general, there will be a recursive rule for skipping elements of the list that do not occur in the sublist (rule 3). This rule is only needed as long as there are still elements in the sublist, so a constraint would be beneficial, that prevents this rule from succeeding once the sublist is empty. These ideas can be realized in Prolog like so:
sum_ofsub_fromlist(0,[],_L). % rule 1
sum_ofsub_fromlist(X,[A|Bs],[A|As]) :- % rule 2
X0 is X-A,
sum_ofsub_fromlist(X0,Bs,As).
sum_ofsub_fromlist(X,Bs,[_A|As]) :- % rule 3
dif(Bs,[]), % constraint: sublist not empty
sum_ofsub_fromlist(X,Bs,As).
You can query this predicate to assure yourself that it delivers all sublists for the given sum in your examples:
?- sum_ofsub_fromlist(6,S,[1,2,3,4,5,6]).
S = [1, 2, 3] ;
S = [1, 5] ;
S = [2, 4] ;
S = [6] ;
false.
?- sum_ofsub_fromlist(12,S,[2,5,7,9]).
S = [5, 7] ;
false.
Building on this you can then write a calling predicate that only succeeds for sublists of length three:
sum_oftriple_fromlist(S,T,L) :-
T=[_,_,_], % T has to be a triple
sum_ofsub_fromlist(S,T,L).
This predicate yields the answers you desire:
?- sum_oftriple_fromlist(6,T,[1,2,3,4,5,6]).
T = [1, 2, 3] ;
false.
?- sum_oftriple_fromlist(12,T,[2,5,7,9]).
false.
Note that the predicate is also working with negative numbers:
?- sum_oftriple_fromlist(6,T,[-5,-3,-1,2,4,7,8,9]).
T = [-5, 2, 9] ;
T = [-5, 4, 7] ;
T = [-3, 2, 7] ;
false.
?- sum_oftriple_fromlist(-6,T,[-6,-5,-4,-3,-2,-1,2,4]).
T = [-6, -4, 4] ;
T = [-6, -2, 2] ;
T = [-5, -3, 2] ;
T = [-3, -2, -1] ;
false.
However, due to the use of is/2, the predicate only works if the first and the third arguments are ground:
?- sum_oftriple_fromlist(S,T,[1,2,3,4,5,6]).
ERROR: is/2: Arguments are not sufficiently instantiated
Exception: (7) sum_ofsub_fromlist(_G918, [_G1016, _G1019, _G1022], [1, 2, 3, 4, 5, 6]) ?
?- sum_oftriple_fromlist(6,T,[A,B,C,D,E,F]).
ERROR: is/2: Arguments are not sufficiently instantiated
Exception: (7) sum_ofsub_fromlist(6, [_G2121, _G2124, _G2127], [_G1945, _G1948, _G1951, _G1954, _G1957, _G1960]) ?
If that's fine with you, you can stop here. Alternatively, you could opt to make the predicate more versatile by using CLP(FD). Just apply these minor changes to your code:
:- use_module(library(clpfd)). % <- new
sum_oftriple_fromlist(S,T,L) :-
T=[_,_,_],
sum_ofsub_fromlist(S,T,L).
sum_ofsub_fromlist(0,[],_L).
sum_ofsub_fromlist(X,[A|Bs],[A|As]) :-
X0 #= X-A, % <- change
sum_ofsub_fromlist(X0,Bs,As).
sum_ofsub_fromlist(X,Bs,[_A|As]) :-
dif(Bs,[]),
sum_ofsub_fromlist(X,Bs,As).
Now the above queries deliver answers:
?- sum_oftriple_fromlist(S,T,[1,2,3,4,5,6]).
S = 6,
T = [1, 2, 3] ;
S = 7,
T = [1, 2, 4] ;
S = 8,
T = [1, 2, 5] ;
. % another
. % seventeen
. % results here
The second query, however, yields residual goals (see documentation for details) as results:
?- sum_oftriple_fromlist(6,T,[A,B,C,D,E,F]).
T = [A, B, C],
_G2424+A#=6,
C+B#=_G2424 ;
T = [A, B, D],
_G2424+A#=6,
D+B#=_G2424 ;
.
.
.
To get actual numbers, you have to restrict the range of the numbers and subsequently label the variables in the list:
?- L=[A,B,C,D,E,F], sum_oftriple_fromlist(6,T,L), L ins 1..6, label(L).
L = [1, 1, 4, 1, 1, 1],
A = B, B = D, D = E, E = F, F = 1,
C = 4,
T = [1, 1, 4] ;
L = [1, 1, 4, 1, 1, 2],
A = B, B = D, D = E, E = 1,
C = 4,
F = 2,
T = [1, 1, 4] ;
.
.
.
Possibly you are only interested in lists where every number only appears once:
?- L=[A,B,C,D,E,F], all_distinct(L), sum_oftriple_fromlist(6,T,L), L ins 1..6, label(L).
L = [1, 2, 3, 4, 5, 6],
A = 1,
B = 2,
C = 3,
D = 4,
E = 5,
F = 6,
T = [1, 2, 3] ;
L = [1, 2, 3, 4, 6, 5],
A = 1,
B = 2,
C = 3,
D = 4,
E = 6,
F = 5,
T = [1, 2, 3] ;
.
.
.
Or maybe you don't even want to restrict the sum:
?- L=[A,B,C,D,E,F], all_distinct(L), sum_oftriple_fromlist(S,T,L), L ins 1..6, label(L).
L = [1, 2, 3, 4, 5, 6],
A = 1,
B = 2,
C = 3,
D = 4,
E = 5,
F = S, S = 6, % sum = 6
T = [1, 2, 3] ;
.
.
.
L = [1, 2, 4, 3, 5, 6],
A = 1,
B = 2,
C = 4,
D = 3,
E = 5,
F = 6,
S = 7, % sum = 7
T = [1, 2, 4] ;
.
.
.
As you can see the CLP(FD) version of the predicate resembles a true relation as opposed to the non-CLP(FD) version. And of course your example queries yield the same answers with both versions.
Your code only considers the first 3 items in the list, and not any other combinations.
The most natural way to structure a solution involving a list is to base your recursion on the structure of the list. So:
If the first element of the list (say, X) is to be included in the 3 values that sum to N, we need to find a way to find 2 values in the rest of the list that sum to N-X.
If it isn't, just try to solve the problem using the rest of the list.
Note that you may need a "helper" version of your predicate that allows you to add other parameters. In this case, knowing how many values you need to add up would be helpful.

How would I modify my predicate to jumble my output result?

So I'm experimenting with some stuff. I have the following simple predicate:
insert([],Y,[Y]).
insert([H|T],Y,[H,Y|T]).
So this'll insert my element Y into my list. However this always puts it in the same place, which is in the middle. But say I wanted it to be more like the following:
?- insert([1,2,3], 4, Zs).
should succeed four times and give the following answers:
Zs = [4, 1, 2, 3]
Zs = [1, 4, 2, 3]
Zs = [1, 2, 4, 3]
Zs = [1, 2, 3, 4].
How would I modify my predicate accordingly?
another useful builtin, extended in SWI-Prolog to handle insertion as well as selection:
?- nth1(_,X,a,[1,2,3]).
X = [a, 1, 2, 3] ;
X = [1, a, 2, 3] ;
X = [1, 2, a, 3] ;
X = [1, 2, 3, a] ;
false.
just ignore first argument (the index itself)
If you want to find possible position of an element in a list, then you have to find all possible concatenation of this list containing the element to insert. This can be described using append/3 predicate:
insert(X,Y,Z):- append(A, B, X), append(A, [Y|B], Z).
This predicate states that exists a concatenation of two sublist that returns list X, and this two sublist concatenated with the value Y in the head of the second sublist, returns list Z.
?- insert([1,2,3], 4, Z).
Z = [4, 1, 2, 3]
Z = [1, 4, 2, 3]
Z = [1, 2, 4, 3]
Z = [1, 2, 3, 4]
false

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