Reputation: 77
Define a predicate subsetsum(L, Sum, Subl) that takes a list L of numbers, a number Sum, and unifies SubL with a sub sequence of L such that the sum of the numbers in SubL is Sum.
For example
?- subsetsum([1,2,5,3,2], 5, SubSet);
SubSet = [1,2,2];
SubSet = [2,3];
SubSet = [5];
SubSet = [3,2];
No.
we have
sum([H1 | [H2 | Tail]], S):-
sum([[H1+H2]|Tail], S):-
sum([X], X).
and
subset([],[]).
subset([H1|T1], [H1|T2]) :- // heads are the same
subset(T1, T2).
subset([_|Rest], X):
subset(Rest, X).
Upvotes: 1
Views: 2792
Reputation: 18726
If all numbers used are integers and your Prolog processor supports clpfd, proceed like this!
:- use_module(library(clpfd)). z_z_product(A,B,AB) :- AB #= A*B. subsetsum_(Zs, Sum, Bs, [Sum|Vs]) :- same_length(Zs, Bs), append(Zs, Bs, Vs), Bs ins 0..1, maplist(z_z_product, Zs, Bs, Xs), sum(Xs, #=, Sum).
Sample query:
?- subsetsum_([1,2,5,3,2], 5, Sel, Vs), labeling([], Vs). Sel = [0,0,0,1,1], Vs = [5,1,2,5,3,2,0,0,0,1,1] ; Sel = [0,0,1,0,0], Vs = [5,1,2,5,3,2,0,0,1,0,0] ; Sel = [0,1,0,1,0], Vs = [5,1,2,5,3,2,0,1,0,1,0] ; Sel = [1,1,0,0,1], Vs = [5,1,2,5,3,2,1,1,0,0,1] ; false.
Upvotes: 3
Reputation: 18726
With swi-prolog we can use the library predicate sum_list/2
together with that subset/2
you already got! Note that I gave subset/2
the better fitting name list_subsequence/2
:
list_subsequence([], []). list_subsequence([X|Xs], [X|Ys]) :- list_subsequence(Xs, Ys). list_subsequence([_|Xs], Ys) :- list_subsequence(Xs, Ys). subsetsum(List, Sum, Sub) :- list_subsequence(List, Sub), sum_list(Sub, Sum).
Here is the sample query that you gave:
?- subsetsum([1,2,5,3,2], 5, Xs).
Xs = [1,2,2]
; Xs = [2,3]
; Xs = [5]
; Xs = [3,2]
; false.
OK! Let's run another query with both integers and floats... does that work, too?
?- subsetsum([1,2.1,5,3,2], 5.1, Xs). Xs = [1,2.1,2] ; Xs = [2.1,3] ; false.
Looks alright to me!
Upvotes: 1
Reputation: 4199
The following clauses should do what you need...
subsetsum(SET, SUM, ANSWER) :-
% Find a subset
subset(SET, ANSWER),
% Check elements of the subset add up to SUM
sum(ANSWER, SUM).
% sum(LIST, SUM) - sums all numbers in the list
sum([], 0).
sum([X | T], SUM) :-
sum(T, TAILSUM),
SUM is TAILSUM + X.
% subset - finds subsets
subset([], []).
subset([E|Tail], [E|NTail]) :-
subset(Tail, NTail).
subset([_|Tail], NTail) :-
subset(Tail, NTail).
Upvotes: 2