sail0r
sail0r

Reputation: 451

How to use an fd solver to determine which elements of a list can sum to a given number?

Given a list of possible summands I want to determine which, if any, can form a given sum. For example, with [1,2,3,4,5] I can make the sum of 9 with [4,5], [5,3,1], and [4,3,2]. I am using GNU Prolog and have something like the following which does not work

numbers([1,2,3,4,5]).

all_unique(_, []).
all_unique(L, [V|T]) :-
    fd_exactly(1, L, V),
    all_unique(L, T).

fd_sum([], Sum).
fd_sum([H|T], Sum):-
    S = Sum + H,
    fd_sum(T, S).
    
sum_clp(N, Summands):-
    numbers(Numbers),
    length(Numbers, F),
    between(1, F, X),
    length(S, X),
    fd_domain(S, Numbers),
    fd_domain(Y, [N]),
    all_unique(S, Numbers),
    fd_sum(S, Sum),
    Sum #= Y,
    fd_labeling(S).

I think the main problem is that I am not representing the constraint on the sum properly? Or maybe it is something else?

Upvotes: 1

Views: 130

Answers (2)

CapelliC
CapelliC

Reputation: 60014

Just in case you're really interested in CLP(FD), here is your corrected program.

numbers([1,2,3,4,5]).

% note: use builtins where available, both for efficiency and correctness
%all_unique(_, []).
%all_unique(L, [V|T]) :-
%    fd_exactly(1, L, V),
%    all_unique(L, T).

fd_sum([], 0). % sum_fd_SO.pl:8: warning: singleton variables [Sum] for fd_sum/2
fd_sum([H|T], Sum):-
    % note: use CLP(FD) operators and the correct operands
    Sum #= S + H,
    fd_sum(T, S).
    
sum_clp(N, S):- % sum_fd_SO.pl:13-23: warning: singleton variables [Summands] for sum_clp/2
    numbers(Numbers),
    length(Numbers, F),
    between(1, F, X),
    length(S, X),
    fd_domain(S, Numbers),
    %fd_domain(Y, [N]),
    %all_unique(S, Numbers),
    fd_all_different(S),
    fd_sum(S, N),
    %Sum #= Y,
    fd_labeling(S).

test

 ?- sum_clp(3,L).

L = [3] ? ;

L = [1,2] ? ;

L = [2,1] ? ;

no

Upvotes: 2

rajashekar
rajashekar

Reputation: 3753

I think mixing the code for sublist into clp code is causing some confusion. GNU-Prolog has a sublist/2 predicate, you can use that.

You seem to be building the arithmetic expression with fd_sum but it is incorrectly implemented.

sum_exp([], 0).
sum_exp([X|Xs], X+Xse) :-
    sum_exp(Xs, Xse).

sum_c(X, N, Xsub) :-
    sublist(Xsub, X),
    sum_exp(Xsub, Xe),
    N #= Xe.
| ?- sum_exp([A, B, C, D], X).

X = A+(B+(C+(D+0)))

yes

| ?- sum_c([1, 2, 3, 4, 5], 9, X).

X = [4,5] ? ;

X = [2,3,4] ? ;

X = [1,3,5] ? ;

(1 ms) no

| ?- length(X, 4), sum_c(X, 4, [A, B]), member(A, [1, 2, 3]).

A = 1
B = 3
X = [_,_,1,3] ? ;

A = 2
B = 2
X = [_,_,2,2] ? ;

A = 3
B = 1
X = [_,_,3,1] ? 

yes

Upvotes: 2

Related Questions