Prolog 如何创建与特定数字相加的数字列表

Prolog 如何创建与特定数字相加的数字列表,prolog,Prolog,我需要一些帮助,在Prolog中编写一个谓词,给定一个数字作为输入,返回一个列表列表,其中的数字相加 让我们调用谓词addUpList/2,它的工作原理如下: ?- addUpList(3,P). P = [[1,2], [2,1], [1,1,1]]. % expected result 我很难弄明白这一点,我开始认为这是不可能的。有什么想法吗?提前感谢。试试这个: condense([], Rs, Rs). condense([X|Xs], Ys, Zs) :- con

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

让我们调用谓词addUpList/2,它的工作原理如下:

?- addUpList(3,P).
P = [[1,2], [2,1], [1,1,1]].       % expected result
我很难弄明白这一点,我开始认为这是不可能的。有什么想法吗?提前感谢。

试试这个:

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).

让我知道我得到了什么分数。:-)

规则
num\u split/2
生成将数字拆分为列表的方法,其中第一个元素
X
1
N
之间的任何数字,列表的其余部分是
N-X
的拆分

num_split(0, []).
num_split(N, [X | List]) :-
    between(1, N, X),
    plus(X, Y, N),
    num_split(Y, List).
要获得所有这些拆分,只需在
num\u split/2
上调用
findall/3

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的帖子,该帖子给出了相同的答案,并进行了更多的解释。

问题中给出的示例表明,任何正整数N≤需要10人。然而,请注意,N=3的解[3]似乎被忽略了。N的值是2^(N-1),因此N=10给出了一个长列表,但不是一个无法管理的列表

还需要将所有此类解决方案收集到一个列表中,在我们编写生成它们的谓词
composition/2
之后,
findall/3
通常可以这样做

其思想是选择第一个和,介于1和N之间的任何值,从总数中减去它并递归(当总数为零时,以空列表停止)。SWI Prolog提供了一个介于/3之间的谓词
,它可以生成那些可能的第一个求和,以及Amzi!Prolog为/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]] 

当然,对于特定的应用程序,可能需要这样的解决方案的列表或省略单列表,但是这不清楚,因为这个问题现在已经措辞了。

已经有很多很好的答案来回答这个问题,但是这里有另一个解决这个问题的方法供您考虑。该程序与其他程序的不同之处在于,它非常有效,并生成列表的非冗余解决方案,这些列表被假定为表示整数集,这些整数集相加到指定的数字

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(4,L).
L = [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]].
请注意,包含一个
2
和两个
1
s的列表在此结果集中只出现一次;这是因为
gen/4
计算唯一的整数集,这些整数加起来就是指定的数字

介于 和

就像@sharky的代码一样,我们在相邻的列表项之间强加了一种排序关系,以限制解决方案空间的大小——如果需要,我们知道如何膨胀它。因此@sharky的
breakdown/2
gen/2
的解决方案集是相等的(忽略列表反转)

至于性能,请考虑:

?- 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. ?-时间((分解(40,u),假))。 %861232个推论,0.066秒内0.066 CPU(100%CPU,13127147) 错。 ?-时间((gen(40,u),假))。 %8580839推断,0.842秒内0.842 CPU(100%CPU,10185807) 错。
这不是不可能的,但您不会希望对非常大的整数运行此操作!从阅读中可以获得一些见解。嗯,我明白了。。。但这些答案取决于向他们传递一组数字作为输入。在我的例子中,我需要生成一组数字。。。我不知道我是否说得很清楚。不,你说得很清楚;另一个问题解决给定加法器的子集和问题,而您需要所有的组合。这是相似的,但不是相同的。你的问题将产生巨大的列表。只是出于好奇,这是家庭作业吗?您希望如何处理结果列表的元素。你知道对于一个给定的和,结果列表有多大吗?我知道它可以生成巨大的列表,但我只需要加在[1…10]之间的加数(使列表尽可能大[1,1,1,1,1,1,1,1,1])。整个列表将被传递到另一个谓词,以便构建一个R元树。坦率地说,是的,这是一个家庭作业,我至少一周来一直在努力解决这个问题。我想说的是,这与枚举所有可能的整数分区有关,有几种已知的方法可以做到这一点。包含相关标签会很有帮助。天哪!就是这样!我想这正是我需要的,我得仔细看看。非常感谢你! ?- 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.