我正在试图弄清楚如何生成集合列表,其中每个集合的长度为N,每个集合的总和为X.
我找到了这段代码:
num_split(0,[]).
num_split(N, [X | List]):-
between(1,N,X),
plus(X,Y,N),
num_split(Y,List).
我可以使用它来获得总和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.
问题是这些都是排列,我正在寻找组合。我正在寻找的输出应该是get_combos(Sum,Length,List)
:
get_combos(6,2,List).
List = [5,1];
List = [4,2];
List = [3,3];
false.
任何指针?
答案 0 :(得分:3)
如果您有权访问CLP(FD)库,则可以使用以下代码:
:- [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).
试验:
?- get_combos(10,3,L).
L = [1, 2, 7] ;
L = [1, 3, 6] ;
L = [1, 4, 5] ;
L = [2, 3, 5] ;
也许我误解了你的问题。使用此链
...
chain(List, #=<),
....
获取可能的重复值:
?- 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.
答案 1 :(得分:1)
在数组中的连续值之间强制实施“等于或大于”的限制。
您可以将其添加为另一个谓词:
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).
不幸的是,在num_split / 3结束时添加它并不一定会提高它的性能,因此将其直接添加到算法中会稍微好一些:
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.
由于小于或等于运算符(=&lt;)要求两个操作数完全实例化,因此我不确定这会得到多少性能,因为比较必须在递归之后。它起作用。