Prolog中的数字分解

Number decomposition in Prolog

我是 Prolog 的新手,我有以下问题:如何将自然数 N 分解为一个列表,其中包含总和等于 N 的连续自然数?

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
N=10, R=[1,2,3,4];

N=80, R=[14, 15, 16, 17, 18];

N=99, R=[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

      R=[7, 8, 9, 10, 11, 12, 13, 14, 15]

      R=[14, 15, 16, 17, 18, 19]

      R=[32, 33, 34]

      R=[49, 50]

编辑:
我试图在不使用默认方法的情况下构建列表,到目前为止我设法写了这篇文章:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cand([H|_],H).
cand([_|T],E):-
    cand(T,E).

suma([],0).
suma([H|T],S):-
    suma(T,Temp),
    S is Temp+H.

list(0,[]).
list(N,[Nr|R]):-
    Nr is N-1,
    list(Nr,R).

generate(_,_,A,_,A).
generate(N,L,[H|T],S,R):-
    S<N,
    cand(L,E),
    not(cand([H|T],E)),
    E=:=H-1,
    suma([H|T],S),
    generate(N,L,[E,H|T],S,R).
start(N,Rez):-
    list(N,L),
    cand(L,E1),
    cand(L,E2),
    E2=:=E1-1,
    generate(N,L,[E2,E1],0,Rez).

但由于某种原因,无论我输入多少数字,结果总是空列表。


这是@mat\\'s previous answer的后续。
想要速度吗?使用这样的冗余约束!

1
2
3
4
5
6
7
8
9
10
11
12
13
n_list(N, Ls) :-
    L #=< N,
    L #>  0,
    L0 #= L-1,
    N0 #= (L0*L0+L0)//2,
    N  #= N0+K*L,
    K #>= 0,
    indomain(L),
    length(Ls, L),
    Ls ins 0..N,
    foldl(consecutive, Ls, _, _),
    sum(Ls, #=, N),
    label(Ls).

无冗余约束的运行时:

1
2
3
?- time((N in 1..100,indomain(N),n_list(N,_),false)).
% 1,048,270,907 inferences, 85.594 CPU in 85.552 seconds (100% CPU, 12247032 Lips)
false.

具有冗余约束的运行时:

1
2
3
?- time((N in 1..100,indomain(N),n_list(N,_),false)).
% 10,312,514 inferences, 0.834 CPU in 0.833 seconds (100% CPU, 12369051 Lips)
false.

使用 clpfd 约束看看有比你显示的更多的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
:- use_module(library(clpfd)).

n_list(N, Ls) :-
    L #=< N,
    L #> 0,
    indomain(L),
    length(Ls, L),
    Ls ins 0..N,
    foldl(consecutive, Ls, _, _),
    sum(Ls, #=, N),
    label(Ls).

consecutive(A, Prev, A) :- A #= Prev + 1.

示例:

1
2
3
4
5
?- n_list(10, Ls).
Ls = [10] ;
Ls = [1, 2, 3, 4] ;
Ls = [0, 1, 2, 3, 4] ;
false.

另一个例子:

1
2
3
4
?- n_list(80, Ls).
Ls = [80] ;
Ls = [14, 15, 16, 17, 18] ;
false.

我把加快速度作为练习留给你。