3

This is the problem statement:

Given a list of integers L and an integer S, generate all sublists whose elements add up to S.

This is my solution:

domains
        list=integer*
        clist=list*
predicates
        sum(list,integer)
        check(clist,clist,integer)
        gensub(list,list)
        getsub(list,clist,integer)
clauses
        %gets all possible subsets with gensub and findall, binds them to
        %and then checks the subsets to see if the elements add up to S
        %and adds the correct ones to Rez.
        %Goal example: getsub([1,2,5,6],X,7)
        getsub([], [], _).
        getsub(L, Rez, S):-
                findall(LR, gensub(L,LR), X),
                check(X, Rez, S).

        %generates all possible subsets of the given set
        gensub([], []).
        gensub([E|Tail], [E|NTail]):-
                gensub(Tail, NTail).
        gensub([_|Tail], NTail):-
                gensub(Tail, NTail).

        %checks if the sum of the elements of a list is S
        sum([], S):-S=0.
        sum([H|T], S):-
                N=S-H,
                sum(T, N).

        %checks if each sublist of a given list of lists, has the sum of elements S
        check([], [], S).
        %the LR variable here gives a warning after entering the goal
        check([H|T], [H|LR], S):-
                sum(H, S).

So after I run it and I'm asked for the Goal, I try to do getsub([1,2,5,6],X,7) so that I get all subsets whose elements add up to 7, but I get a No Solution, and a warning on the LR variable in the check clause, the variable is not bound. I'm not sure what I'm doing wrong or if there is an easier solution to this. Any help is appreciated.

Shevliaskovic
  • 1,562
  • 4
  • 26
  • 43
rares.urdea
  • 650
  • 3
  • 16
  • 26
  • @dasblinkenlight It is indeed turbo-Prolog, I use the borland version. – rares.urdea Nov 20 '13 at 10:10
  • Consider changing the algorithm: rather than generating all pairs and checking the sums, go through the first list elements `E`, and keep elements of the second list that are equal to `S-E`. – Sergey Kalinichenko Nov 20 '13 at 10:25

2 Answers2

3

I think your last procedure should read

%checks if each sublist of a given list of lists, has the sum of elements S
check([], [], _).
%the LR variable here gives a warning after entering the goal
check([H|T], [H|LR], S):-
    sum(H, S),
    !, check(T, LR, S).
check([_|T], LR, S):-
    check(T, LR, S).

and then, some note:

  • why generate all subsets, then filter out ? 'push' the test inside findall, it's easier and faster
  • you have a sum/2 predicate that could be much more useful if it does what it says to do. Here is it:

--

% sum elements of a list
sum([], 0).
sum([H|T], S):-
    sum(T, N),
    S is N+H.

(to be true, I tested using this one, getting [[1,6],[2,5]])

CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • I was half-way through to a similar answer myself, but I've flagged yours as the correct one since mine was not complete. Also, thanks for the notes, I'll consider improving the program itself. – rares.urdea Nov 20 '13 at 10:26
2

In this answer we use :

:- use_module(library(clpfd)).

To get to all possible sublists, we use sublist_of/2:

sublist_of(Sub,List) :-
   list_sub(List,Sub).

sublist_of/2 is based on auxiliary predicate list_sub/2:

list_sub([]    ,[]).
list_sub([_|Xs],Ys) :-
   list_sub(Xs,Ys).
list_sub([X|Xs],[X|Ys]) :-
   list_sub(Xs,Ys).

Let's put it together!

list_sublist_sum(Xs,Zs,Sum) :-
   sublist_of(Zs,Xs),
   sum(Zs,#=,Sum).

Sample query:

?- list_sublist_sum([3,34,4,12,5,2],Zs,9).
  Zs = [4,5]
; Zs = [3,4,2]
; false.
repeat
  • 18,496
  • 4
  • 54
  • 166
  • 1
    How would one make this efficient with sum = 666 and list of integers 1...255 – John Carlson May 31 '22 at 09:44
  • @JohnCarlson. The list you are using is `[1,2,3,...,254,255]`? – repeat Jun 01 '22 at 11:59
  • @JohnCarlson. This would make a good (new/separate) question, don't you think? – repeat Jun 02 '22 at 17:29
  • 1
    Yes, [1,2,3,...,254,255] – John Carlson Jun 10 '23 at 04:21
  • @JohnCarlson. Well in that case you could use something like this. I'm using SICStus Prolog in my query `?- use_module([library(clpfd),library(lists)]), length(Zs,N), domain(Zs,1,255), ordered(#>,Zs), sum(Zs,#=,666), labeling([],Zs).` Gives me answers like `Zs = [223,222,221], N = 3` basically in no time:) – repeat Jun 16 '23 at 12:28