在prolog中编写一个程序,确定是否正好有三个值,它们加起来为N的总和

时间:2018-04-29 23:44:28

标签: prolog

例如:

列表[1,2,3,4,5,6],N等于6应打印 true ,因为正好有3个值加起来为6. 1 + 2 + 3。< / p>

N等于12的列表[2,5,7,9]应打印 false ,因为没有3个元素加起来为12。

2 个答案:

答案 0 :(得分:2)

我们可以从更一般的谓词开始,该谓词描述列表,所述列表的子列表和子列表中的数字之和之间的关系。为了明确哪个参数是什么,为谓词选择描述性名称是合适的,比如说sum_ofsub_fromlist/3。现在让我们观察如果第一个参数是子列表中数字的总和,则从该和中连续地减去这些数字会产生零,例如:X = A + B→X-A-B = 0。因此,将有一个基本案例包含0作为和[]作为子列表(规则1)和递归规则,从总和中减去子列表的元素(规则2)。并且由于子列表不包含从一般情况下取得的列表的所有元素,因此将存在用于跳过列表中未出现在子列表中的元素的递归规则(规则3)。只要子列表中仍有元素,只需要此规则,因此约束将是有益的,一旦子列表为空,就会阻止此规则成功。这些想法可以像Prolog一样在Prolog中实现:

sum_ofsub_fromlist(0,[],_L).             % rule 1
sum_ofsub_fromlist(X,[A|Bs],[A|As]) :-   % rule 2
   X0 is X-A,
   sum_ofsub_fromlist(X0,Bs,As).
sum_ofsub_fromlist(X,Bs,[_A|As]) :-      % rule 3
   dif(Bs,[]),                           % constraint: sublist not empty
   sum_ofsub_fromlist(X,Bs,As).

您可以查询此谓词以确保自己在示例中提供给定总和的所有子列表:

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

?- sum_ofsub_fromlist(12,S,[2,5,7,9]).
S = [5, 7] ;
false.

在此基础上,您可以编写一个仅对长度为3的子列表成功的调用谓词:

sum_oftriple_fromlist(S,T,L) :-
   T=[_,_,_],                      % T has to be a triple
   sum_ofsub_fromlist(S,T,L).

这个谓词产生了你想要的答案:

?- sum_oftriple_fromlist(6,T,[1,2,3,4,5,6]).
T = [1, 2, 3] ;
false.

?- sum_oftriple_fromlist(12,T,[2,5,7,9]).
false.

请注意,谓词也使用负数:

?- sum_oftriple_fromlist(6,T,[-5,-3,-1,2,4,7,8,9]).
T = [-5, 2, 9] ;
T = [-5, 4, 7] ;
T = [-3, 2, 7] ;
false.

?- sum_oftriple_fromlist(-6,T,[-6,-5,-4,-3,-2,-1,2,4]).
T = [-6, -4, 4] ;
T = [-6, -2, 2] ;
T = [-5, -3, 2] ;
T = [-3, -2, -1] ;
false.

但是,由于使用了is/2,谓词只有在第一个和第三个参数被接地时才有效:

?- sum_oftriple_fromlist(S,T,[1,2,3,4,5,6]).
ERROR: is/2: Arguments are not sufficiently instantiated
   Exception: (7) sum_ofsub_fromlist(_G918, [_G1016, _G1019, _G1022], [1, 2, 3, 4, 5, 6]) ?

?- sum_oftriple_fromlist(6,T,[A,B,C,D,E,F]).
ERROR: is/2: Arguments are not sufficiently instantiated
   Exception: (7) sum_ofsub_fromlist(6, [_G2121, _G2124, _G2127], [_G1945, _G1948, _G1951, _G1954, _G1957, _G1960]) ?

如果这对您没问题,您可以在这里停下来。或者,您可以选择使用CLP(FD)使谓词更具通用性。只需对代码应用这些小修改:

:- use_module(library(clpfd)).               % <- new

sum_oftriple_fromlist(S,T,L) :-
   T=[_,_,_],
   sum_ofsub_fromlist(S,T,L).

sum_ofsub_fromlist(0,[],_L).
sum_ofsub_fromlist(X,[A|Bs],[A|As]) :-
   X0 #= X-A,                                % <- change
   sum_ofsub_fromlist(X0,Bs,As).
sum_ofsub_fromlist(X,Bs,[_A|As]) :-
   dif(Bs,[]),
   sum_ofsub_fromlist(X,Bs,As).

现在上述查询提供了答案:

?- sum_oftriple_fromlist(S,T,[1,2,3,4,5,6]).
S = 6,
T = [1, 2, 3] ;
S = 7,
T = [1, 2, 4] ;
S = 8,
T = [1, 2, 5] ;
.                     % another
.                     % seventeen
.                     % results here

然而,第二个查询产生了剩余目标(详见documentation)结果:

?- sum_oftriple_fromlist(6,T,[A,B,C,D,E,F]).
T = [A, B, C],
_G2424+A#=6,
C+B#=_G2424 ;
T = [A, B, D],
_G2424+A#=6,
D+B#=_G2424 ;
.
.
.

要获得实际数字,您必须限制数字的范围,然后在列表中标记变量:

?- L=[A,B,C,D,E,F], sum_oftriple_fromlist(6,T,L), L ins 1..6, label(L).
L = [1, 1, 4, 1, 1, 1],
A = B, B = D, D = E, E = F, F = 1,
C = 4,
T = [1, 1, 4] ;
L = [1, 1, 4, 1, 1, 2],
A = B, B = D, D = E, E = 1,
C = 4,
F = 2,
T = [1, 1, 4] ;
.
.
.

可能您只对每个号码只出现一次的列表感兴趣:

?- L=[A,B,C,D,E,F], all_distinct(L), sum_oftriple_fromlist(6,T,L), L ins 1..6, label(L).
L = [1, 2, 3, 4, 5, 6],
A = 1,
B = 2,
C = 3,
D = 4,
E = 5,
F = 6,
T = [1, 2, 3] ;
L = [1, 2, 3, 4, 6, 5],
A = 1,
B = 2,
C = 3,
D = 4,
E = 6,
F = 5,
T = [1, 2, 3] ;
.
.
.

或许你甚至不想限制总和:

?- L=[A,B,C,D,E,F], all_distinct(L), sum_oftriple_fromlist(S,T,L), L ins 1..6, label(L).
L = [1, 2, 3, 4, 5, 6],
A = 1,
B = 2,
C = 3,
D = 4,
E = 5,
F = S, S = 6,                 % sum = 6
T = [1, 2, 3] ;
.
.
.
L = [1, 2, 4, 3, 5, 6],
A = 1,
B = 2,
C = 4,
D = 3,
E = 5,
F = 6,
S = 7,                        % sum = 7
T = [1, 2, 4] ;
.
.
.

正如您所看到的,谓词的CLP(FD)版本类似于真正的关系而非非CLP(FD)版本。当然,您的示例查询会在两个版本中产生相同的答案。

答案 1 :(得分:0)

您的代码仅考虑列表中的前3项,而不考虑任何其他组合。

构建涉及列表的解决方案的最自然的方法是将递归基于列表的结构。所以:

  1. 如果列表中的第一个元素(比如说X)要包含在总和为N的3个值中,我们需要找到一种方法,在列表的其余部分中找到总和为NX的2个值。 / LI>
  2. 如果不是,请尝试使用列表的其余部分解决问题。
  3. 请注意,您可能需要谓词的“帮助”版本,以允许您添加其他参数。在这种情况下,知道需要添加多少个值会有所帮助。