如何创建添加到特定数字的数字列表

时间:2011-07-26 04:21:25

标签: prolog

我需要一些帮助在Prolog中编写一个谓词,给定一个数字作为输入,返回一个包含数字的列表列表。

让我们调用谓词 addUpList / 2 ,它应该像这样工作:

?- addUpList(3,P).
P = [[1,2], [2,1], [1,1,1]].       % expected result

我很难解决这个问题,我开始认为这是不可能的。有任何想法吗?提前谢谢。

5 个答案:

答案 0 :(得分:4)

试试这个:

condense([], Rs, Rs).
condense([X|Xs], Ys, Zs) :-
    condense(Xs, [X|Ys], Zs).
condense([X, Y|Xs], Ys, Zs) :-
    Z is X + Y,
    condense([Z|Xs], Ys, Zs).

condense(Xs, Rs) :-
    condense(Xs, [], Rs).

expand(0, []).
expand(N, [1|Ns]) :-
    N > 0,
    N1 is N - 1,
    expand(N1, Ns).

addUpList(N, Zs) :-
    expand(N, Xs),
    findall(Ys, condense(Xs, Ys), Zs).

让我知道我得到了什么标记。 : - )

答案 1 :(得分:2)

规则num_split/2生成将数字拆分为列表的方式,其中第一个元素X1N之间的任意数字以及列表的其余部分是N-X的分割。

num_split(0, []).
num_split(N, [X | List]) :-
    between(1, N, X),
    plus(X, Y, N),
    num_split(Y, List).

要获得所有此类拆分,只需在findall/3上致电num_split/2

add_up_list(N, Splits) :-
    findall(Split, num_split(N, Split), Splits).

用法示例:

?- add_up_list(4, Splits).
Splits =
   [[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4]].

另见@hardmath的帖子,它给出了相同的答案,并提供了更多解释。

答案 2 :(得分:1)

问题中给出的例子表明,任何正整数N≤10都需要compositions (ordered partitions)。但请注意,N = 3的解决方案[3]似乎已被忽略/忽略。 N的number of compositions是2 ^(N-1),因此N = 10给出一个长列表但不是一个无法管理的列表。

还希望将所有此类解决方案收集到列表中,findall/3在编写生成它们的谓词composition/2之后可以执行此操作。

这个想法是选择第一个加数,1和N之间的任何一个,从总和中减去它并递归(当总数达到零时用空列表停止)。 SWI-Prolog提供了一个谓词between/3,它可以生成那些可能的第一个加数,而Amzi! Prolog提供了类似的谓词for/4。为了便于携带,我们在这里编写自己的版本。

summand(Low,High,_) :-
    Low > High,
    !,
    fail.
summand(Low,High,Low).
summand(Low,High,Val) :-
    Now is Low + 1,
    summand(Now,High,Val).

composition(0,[ ]).
composition(N,[H|T]) :-
    summand(1,N,H),
    M is N - H,
    composition(M,T).

鉴于上述Prolog源代码,无论是编译还是解释,都可以通过这种方式获得所有解决方案的列表:

?- findall(C,composition(3,C),L).

C = H126
L = [[1, 1, 1], [1, 2], [2, 1], [3]] 

当然,您的特定申请可能需要安排这样的解决方案清单或遗漏单身人士名单,但由于该问题目前的措辞,这一点并不清楚。

答案 3 :(得分:1)

这个问题已经有很多很好的答案,但是这是你要考虑的另一个解决这个问题的方法。该程序与其他程序的不同之处在于它非常高效,并生成列表的非冗余解决方案,假定它们代表整数的,它们加起来为指定的数字

gen(N, L) :-
    gen(N-1, N, N, FL),
    dup_n(FL, L).
gen(C-F, M, M, [C-F]).
gen(C-F, S, M, [C-F|R]) :-
    S < M, C > 1,
    C0 is C - 1,
    F0 is floor(M / C0),
    S0 is S + (C0 * F0),
    gen(C0-F0, S0, M, R).
gen(C-F, S, M, R) :-
    F > 0,
    F0 is F - 1,
    S0 is S - C,
    gen(C-F0, S0, M, R).

dup_n([], []).
dup_n([_-0|R], L) :-
    !, dup_n(R, L).
dup_n([V-F|R], [V|L]) :-
    F0 is F - 1,
    dup_n([V-F0|R], L).

addUpList/2的实施可以通过以下方式实现:

addUpList(N, P) :-
    findall(L, gen(N, L), P).

哪个应该给你以下行为:

?- addUpList(4,L).
L = [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]].

请注意,包含一个2和两个1的列表仅在此结果集中出现一次;这是因为gen/4计算唯一的整数集合,这些整数累加到指定的数字。

答案 4 :(得分:1)

This answer介于两者之间 @Kaarel's answer@sharky's "efficient" answer

与@ sharky的代码一样,我们强制相邻列表项之间的排序关系来限制解决方案空间的大小---知道如果我们需要如何对其进行膨胀。所以@sharky的gen/2TextField的解决方案集是相等的(忽略列表反转)。

至于表现,请考虑:

?- time((break_down(40,_),false)).
% 861,232 inferences, 0.066 CPU in 0.066 seconds (100% CPU, 13127147 Lips)
false.

?- time((gen(40,_),false)).
% 8,580,839 inferences, 0.842 CPU in 0.842 seconds (100% CPU, 10185807 Lips)
false.