0

I'm working with SWI-Prolog to get clpfd generate the list of all distinct integers between 5 and 10:

q1(Answer) :-
  length(Xs, Answer),
  Xs ins 0..20,
  chain(Xs, #<),
  maplist(q1constraints, Xs).

q1constraints(X) :-
  X #>= 5,
  X #=< 10.

Kind of works, but generates a solution for each of the lengths 0, 1, ... 6 and then hangs seeking a solution of length 7:

?- q1(Answer).
Answer = 0 ;
Answer = 1 ;
Answer = 2 ;
Answer = 3 ;
Answer = 4 ;
Answer = 5 ;
Answer = 6 ;
<hangs>

Is there a good way to generate the list of all integers that satisfy the desired constraints?

false
  • 10,264
  • 13
  • 101
  • 209
  • A list? Or generate them by backtracking? And what do you mean by "distinct", exactly? Why the `length/2`? Why the `ins 0..20`? I have the feeling that what you are not explaining what you are trying to do. –  Apr 12 '16 at 02:44
  • I want to generate, from constraints, the list [5, 6, 7, 8, 9, 10] and then count how many elements are in there. The constraints may be richer, for instance I want to add some form of "even" constraint, possibly "X mod 2 #= 0", which should generate [6, 8, 10]. The list operator may be richer, for example I might want to say "sum" instead of "count". – Cristian Petrescu-Prahova Apr 12 '16 at 15:00
  • PS. Distinct, as in I don't care about lists like [5, 5, 5, 5, 5, 6]. – Cristian Petrescu-Prahova Apr 12 '16 at 15:03

3 Answers3

3

Your question is not that clear to me. But I will try:

?- length(Xs,6), Xs ins 5..10, chain(Xs,#<), Xs = [5|_], last(Xs, 10).
   Xs = [5,6,7,8,9,10].

Note that with these elements, it is necessary to fix the length of the list first. Otherwise:

?- length(Xs,N), Xs ins 5..10, chain(Xs,#<), Xs = [5|_], last(Xs, 10).
   Xs = [5,10], N = 2
;  Xs = [5,_A,10], N = 3, _A in 6..9
;  Xs = [5,_A,_B,10], N = 4, _A#=<_B+ -1, _A in 6..8, _B in 7..9
;  Xs = [5,_C,_A,_B,10], N = 5, _A#=<_B+ -1, _C#=<_A+ -1, _A in 7..8, _C in 6..7, _B in 8..9
;  Xs = [5,6,7,8,9,10], N = 6
;  loops.

In fact, even the (ins)/2 is not needed:

?- length(Xs,6), chain(Xs,#<), Xs = [5|_], last(Xs, 10).
   Xs = [5,6,7,8,9,10].

(In newer versions of clpfd called clpz the argument order of chain/2` is reversed adhering to the common argument ordering.)

false
  • 10,264
  • 13
  • 101
  • 209
  • Thanks for the response. The question is literally "how many positive [even] integers are between 5 and 10". Passing the length as an input defeats the purpose :) I've added "even" predicate to make it slightly more interesting. – Cristian Petrescu-Prahova Apr 12 '16 at 17:28
2

Here is what your program does:

  1. Generate a list of increasing lengths, starting with an empty list
  2. For each element X in the list, pose a constraint that X is in [0, 20]
  3. For the whole list, pose a constraint that values are strictly increasing in magnitude
  4. For each element in the list, pose an additional constraint that X is in [5, 10].

You then ask for the length of the generated list.

There are 6 values that are in [0, 20] and in [5,10]: 5, 6, 7, 8, 9, 10. For the empty list you generate first, there are no constrained variables; for the list with 1 variable, there would be 6 possible values of the variable, but you don't ask for these values, only for the length of the list; for the list with 2 variables, you will have 5 possible combinations: {5,6}, {6,7}, ..., {9,10}, but again, you don't ask for them, just for the length of the list.

Eventually, you get to list with 7 values. Since there are only 6 values that each element could have, there are no solutions.

So what is your goal here? Maybe you should try and explain better. To get all values between 5 and 10 by backtracking, you could say: between(5, 10, X), or, with CLPFD, X in 5..10, label([X]). If it is neither of these, you need to re-write your question.

1

If you want the total number of even integers in 5..10 (SPOILER: there are 3 of them!):

?- aggregate(count, X^(X in 5..10, X mod 2 #= 0, indomain(X)), Answer).
Answer = 3.

Breaking it down:

X in 5..10, X mod 2 #= 0 just constrains X to be an even integer between 5 and 10:

?- X in 5..10, X mod 2 #= 0.
X in 6..10,
X mod 2#=0.

indomain(X) does the actual search, succeeding once for each feasible value of X:

?- X in 5..10, X mod 2 #= 0, indomain(X).
X = 6 ;
X = 8 ;
X = 10.

X^(...) existentially quantifies X within the parentheses, limiting its scope. If we instead leave it as a free variable, aggregation will respect it:

?- aggregate(count, (X in 5..10, X mod 2 #= 0, indomain(X)), Answer).
X = 6,
Answer = 1 ;
X = 8,
Answer = 1 ;
X = 10,
Answer = 1.
GeoffChurch
  • 395
  • 1
  • 13