listsum(L, S) 成功当且仅当 L 是必须从 1 开始的连续整数列表,并且 S 是该列表的总和

发布于 2025-01-15 04:27:15 字数 361 浏览 0 评论 0原文

我正在为即将到来的序言考试而学习,一个我无法弄清楚的练习需要我编写以下谓词:

listsum(L, S) 成功当且仅当 L 是必须从 1 开始的连续整数列表,并且 S 是该列表的总和。

重要的是,当给定 S 时,它能够生成列表。允许使用 numlist/3、is/2 和数学运算符。

例如:

?- listsum(L, 6).
   L = [1,2,3]
;  false.
?- listsum(L, 7).
false.
?- listsum([2,3], 5).
false.
?- listsum([2,4], 6).
false.

你会如何写这个谓词?

编辑:非常感谢大家!

I'm studying for my upcoming prolog exam and one exercise which I can't figure out needs me to write the following predicate:

listsum(L, S) succeeds iff L is a list of consecutive integers which must start at 1 and S is the sum of that list.

It's important that it's able to generate the list when S is given. numlist/3, is/2 and mathematical operators are allowed to be used.

For example:

?- listsum(L, 6).
   L = [1,2,3]
;  false.
?- listsum(L, 7).
false.
?- listsum([2,3], 5).
false.
?- listsum([2,4], 6).
false.

How would you write this predicate?

Edit: Thank you so much to all of you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

晨与橙与城 2025-01-22 04:27:15

如果允许使用库 clpfd,另一个可能的解决方案如下:

:- use_module(library(clpfd)).

listsum(List, Sum) :-
    dif(List, []),
    listsum(Sum, 1, List).

listsum(0, _, []).
listsum(S, X, [X|Xs]) :-
    S #> 0,
    S1 #= S - X,
    X1 #= X + 1,
    listsum(S1, X1, Xs).

一些示例:

?- listsum(L, S).
L = [1],
S = 1 ;
L = [1, 2],
S = 3 ;
L = [1, 2, 3],
S = 6 ;
L = [1, 2, 3, 4],
S = 10 ;
...

?- listsum([1, 2, 3, 4], S).
S = 10 ;
false.

?- listsum(L, 10).
L = [1, 2, 3, 4] ;
false.

?- listsum([1, 2, 3, 4], 10).
true ;
false.

?- listsum([1, 2, 3, 4], 11).
false.

If it is allowed to use library clpfd, another possible solution is as follows:

:- use_module(library(clpfd)).

listsum(List, Sum) :-
    dif(List, []),
    listsum(Sum, 1, List).

listsum(0, _, []).
listsum(S, X, [X|Xs]) :-
    S #> 0,
    S1 #= S - X,
    X1 #= X + 1,
    listsum(S1, X1, Xs).

Some examples:

?- listsum(L, S).
L = [1],
S = 1 ;
L = [1, 2],
S = 3 ;
L = [1, 2, 3],
S = 6 ;
L = [1, 2, 3, 4],
S = 10 ;
...

?- listsum([1, 2, 3, 4], S).
S = 10 ;
false.

?- listsum(L, 10).
L = [1, 2, 3, 4] ;
false.

?- listsum([1, 2, 3, 4], 10).
true ;
false.

?- listsum([1, 2, 3, 4], 11).
false.
蝶舞 2025-01-22 04:27:15

由于前 n 个自然数的总和< /a> 是 s = n(n+1) / 2 我们可以使用有趣的内置 length/2,并编写

listsum_len(L,S) :-
    L=[1|_],
    length(L,N),
    successors(L),
    S is N*(N+1)/2.

successors([_]).
successors([I,J|Ns]) :-
    J is I+1,
    successors([J|Ns]).

涵盖大多数要求的关系:

?- listsum_len([1,2,3],S).
S = 6 ;
false.

?- listsum_len(L,6).
L = [1, 2, 3] .

?- listsum_len(L,S).
L = [1],
S = 1 ;
L = [1, 2],
S = 3 ;

当然是问题是在某些情况下,当通过 length/2 生成列表时,它不会终止。
这会在回溯时发生,例如因为我们明确要求它,或者因为我们导致 length/2 之后的某些调用失败,可能传递了错误的总和。

?- listsum_len(L,3).
L = [1, 2] ;
Action (h for help) ? abort
% Execution Aborted
?- listsum_len(L,2).
Action (h for help) ? abort
% Execution Aborted
?- 

解决这个问题并不容易,但是在需要的地方进行实例化检查,我们得到了正确的解决方案:

listsum([1|L],S) :-
    listsum([1|L],0,S).

listsum([Z],A,S) :-
    S is A+Z.
listsum([I,J|Ns],A,S) :-
    J is I+1,
    B is A+I,
    ( var(S), ! ; B < S ),
    listsum([J|Ns],B,S).

Since the sum of the first n natural numbers is s = n(n+1) / 2 we could use the interesting builtin length/2, and write the relation like

listsum_len(L,S) :-
    L=[1|_],
    length(L,N),
    successors(L),
    S is N*(N+1)/2.

successors([_]).
successors([I,J|Ns]) :-
    J is I+1,
    successors([J|Ns]).

that covers most of requirements:

?- listsum_len([1,2,3],S).
S = 6 ;
false.

?- listsum_len(L,6).
L = [1, 2, 3] .

?- listsum_len(L,S).
L = [1],
S = 1 ;
L = [1, 2],
S = 3 ;

The problem of course is that it will not terminate, under some conditions, when generating the list by means of length/2.
This will happen on backtracking, for instance because we explicitly require it, or because we cause some of the calls following length/2 to fail, maybe passing a wrong sum.

?- listsum_len(L,3).
L = [1, 2] ;
Action (h for help) ? abort
% Execution Aborted
?- listsum_len(L,2).
Action (h for help) ? abort
% Execution Aborted
?- 

It's not easy to solve the problem, but pushing the instantiation check where needed we get a proper solution:

listsum([1|L],S) :-
    listsum([1|L],0,S).

listsum([Z],A,S) :-
    S is A+Z.
listsum([I,J|Ns],A,S) :-
    J is I+1,
    B is A+I,
    ( var(S), ! ; B < S ),
    listsum([J|Ns],B,S).
倾城泪 2025-01-22 04:27:15

假设您可以使用 numlist/3 和数学运算符,并假设您不能使用 clp(FD) ,那么您可以将问题一分为二,一个用于基本总和,另一个用于计算总和计算总和:

listsum(L, S):-
 integer(S),   % ground sum
 End is (-1 + sqrt(1+8*S))/2, % compute ending number
 IEnd is integer(End),
 IEnd =:= End, % checking the sum is an actual valid ending integer
 numlist(1, IEnd, L).
listsum(L, S):-
  var(S),
  numlist2(L, 1, 1, S).
  

numlist2([Cur], Cur, S, S).
numlist2([Cur|L], Cur, CurS, S):-
  Cur1 is Cur + 1,
  CurS1 is CurS + Cur1,
  numlist2(L, Cur1, CurS1, S).

Given you can use numlist/3 and mathematical operators and assuming you cannot use clp(FD) then you may split the problem in two, one for ground sums and other which will compute sums:

listsum(L, S):-
 integer(S),   % ground sum
 End is (-1 + sqrt(1+8*S))/2, % compute ending number
 IEnd is integer(End),
 IEnd =:= End, % checking the sum is an actual valid ending integer
 numlist(1, IEnd, L).
listsum(L, S):-
  var(S),
  numlist2(L, 1, 1, S).
  

numlist2([Cur], Cur, S, S).
numlist2([Cur|L], Cur, CurS, S):-
  Cur1 is Cur + 1,
  CurS1 is CurS + Cur1,
  numlist2(L, Cur1, CurS1, S).
梦境 2025-01-22 04:27:15

虽然不优雅,但如果提供了 nonvar 参数,这将生成并明智地终止

listsum(Lst, Sum) :-
    % Check open list without unintentionally closing it
    (var(Lst) -> true ; \+ \+ (length(Lst, Len), !, numlist(1, Len, Lst))),
    % Reduce unwanted choicepoints
    ((is_list(Lst) ; nonvar(Sum)) -> Once = true ; Once = false),
    listsum_([1|Tail], Tail, Lst, 2, 1, Sum),
    (Once == true -> ! ; true).

% Closing open Lst with empty-list Tail
listsum_(Lst, [], Lst, _Next, Sum, Sum).
listsum_(LU, Tail, Lst, Next, SumU, Sum) :-
    SumU1 is Next + SumU,
    % Terminate if gone past nonvar arguments
    (var(Sum) -> true ; SumU1 =< Sum),
    % Appending to rear of list
    Tail = [Next|Tail1],
    Next1 is Next + 1,
    listsum_(LU, Tail1, Lst, Next1, SumU1, Sum).

导致 swi-prolog:

?- listsum(L, S).
L = [1],
S = 1 ;
L = [1,2],
S = 3 ;
L = [1,2,3],
S = 6 ;
L = [1,2,3,4],
S = 10 ;
L = [1,2,3,4,5],
S = 15 ;

?- listsum(L, 6).
L = [1,2,3].

Whilst inelegant, this will generate, and will also terminate sensibly if nonvar arguments are provided:

listsum(Lst, Sum) :-
    % Check open list without unintentionally closing it
    (var(Lst) -> true ; \+ \+ (length(Lst, Len), !, numlist(1, Len, Lst))),
    % Reduce unwanted choicepoints
    ((is_list(Lst) ; nonvar(Sum)) -> Once = true ; Once = false),
    listsum_([1|Tail], Tail, Lst, 2, 1, Sum),
    (Once == true -> ! ; true).

% Closing open Lst with empty-list Tail
listsum_(Lst, [], Lst, _Next, Sum, Sum).
listsum_(LU, Tail, Lst, Next, SumU, Sum) :-
    SumU1 is Next + SumU,
    % Terminate if gone past nonvar arguments
    (var(Sum) -> true ; SumU1 =< Sum),
    % Appending to rear of list
    Tail = [Next|Tail1],
    Next1 is Next + 1,
    listsum_(LU, Tail1, Lst, Next1, SumU1, Sum).

Result in swi-prolog:

?- listsum(L, S).
L = [1],
S = 1 ;
L = [1,2],
S = 3 ;
L = [1,2,3],
S = 6 ;
L = [1,2,3,4],
S = 10 ;
L = [1,2,3,4,5],
S = 15 ;

?- listsum(L, 6).
L = [1,2,3].
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文