繁体   English   中英

Prolog初学者:如何为谓词中的每个变量制作唯一的值

[英]Prolog Beginner: How to make unique values for each Variable in a predicate

我有一个序言谓词:

Add( [A|B] , Answer ) :-
    ...
    ~ Add everything in the list to come up with answer
    ...

我现在想实现AddUnique ,它将为列表中的所有内容返回唯一值, 除非我两次给变量赋值。


以下是逻辑上等效的内容:

?- AddUnique([A, B, C], 10). 等效于: ?- Add([A, B, C], 10), A != B, B != C, A != C.

和:

?- AddUnique([A, B, B], 10). 等价于: ?- Add([A, B, B], 10), A != B.

也:

?- AddUnique([A, B, B], 10). 等于: ?- Add([A, B, B], 10), A != B, B!=B.


如果?- AddUnique([A,B,C,D], 4). 给定它应该返回false,因为它不能带有加到4的唯一正整数。

如果?- AddUnique([A,A,A,A], 4). 给定它应该返回A=1


问题 :如何在谓词内部移动A != B, B != C, A != C.逻辑而无需执行类似A != A

Khm ...您应该了解doStuff(A,B,C,D)doStuff(A,A,B,B)意思。 首先是要统一值A .. D与这使得适当的值doStuff/4到达目标。 第二个等于A=B, C=D, doStuff(A,B,C,D)doStuff(A,B,C,D), A=B, C=D (但最后一个变体可能会导致回溯)。 因此,我希望您理解不要在doStuff/4内完成unique/1 ,因为它超出了限制。 因此,您应该使用doStuff(A,B,C,D), unique([A,B,C,D])doStuff(A,A,B,B), unique([A,B])

我不知道您如何读A is not B ...无论如何,您可以将unique/1定义为

not_unique([H|T]):- member(H, T) ; not_unique(T).
unique(L):- not(not_unique(L)).

给定您对addUnique/2谓词的描述,可以使用约束逻辑编程来实现解决方案。 不是初学者的东西,但是我还是会发布一个解释。

首先,可能有必要查找什么是约束逻辑编程,以及如何使用实现(例如SWI-PL clpfd )。 基本上,约束逻辑编程(尤其是有限域求解器)将允许您在输入列表上的变量上为addUnique/2指定以下约束:

  1. 输入列表中的变量如何绑定到某些数值(即,从0到指定值的整数)
  2. 输入列表中的不同变量如何不能同时绑定到相同的值(即,!=不同处)
  3. 输入列表中的变量加上所有数字的总和如何必须等于指定的值(即,变量可以在上面的1中取的最大值)。

这些规范一起使基础的约束求解器可以自动确定在上述约束条件下变量可以同时采用的允许值,从而为您提供解决方案(可能有多个,一个或没有)。

这是在SWI-PROLOG中使用上述约束求解器(clpfd求解器)的解决方案:

:- use_module(library(clpfd)).  % import and use the constraint solver library

addUnique([A|As], Val) :-
    unique_vars([A|As], UVs),  % determine all unique variables
    UVs ins 0..Val,            % (1) domain of all unique variables is 0 to Val
    pairwise_inequ(UVs),       % (2) all unique variables are pairwise !=
    construct_sum_constr(As, Val, A, Program),  % (3) construct the sum constraint
    Program,              % assert the sum constraint
    label(UVs).           % label domains to enumerate a solution (backtracks)

% predicate to return a list of unique vars, if present
unique_vars([], []).
unique_vars([V|Vs], [V|Uniq]) :-
    var(V),
    \+ var_memberchk(V, Vs), !,
    unique_vars(Vs, Uniq).
unique_vars([_|Vs], Uniq) :-
    unique_vars(Vs, Uniq).

% predicate to test if a variable appears in a list (possibly including variables)
var_memberchk(V0, [V1|_]) :- 
    V0 == V1, !.
var_memberchk(V0, [_|V1s]) :- 
    var_memberchk(V0, V1s).

% create constraints that assert each in the input list != to each other
pairwise_inequ([]).
pairwise_inequ([V|Vs]) :-
    map_inequ(Vs, V),
    pairwise_inequ(Vs).

% predicate to pairwise assert inequality between all list members
map_inequ([], _).
map_inequ([V1|V1s], V0) :-
    V0 #\= V1,   % the inequality constraint
    map_inequ(V1s, V0).

% predicate to construct a summation constraint, whereby all variables in the 
% input list are constructed into a sum with +/2 and finally equated to a value
construct_sum_constr([], Val, Sum, (Sum #= Val)).
construct_sum_constr([V|Vs], Val, Sum, Program) :-
    construct_sum_constr(Vs, Val, (V + Sum), Program).

运行此代码,例如,将为您提供:

?- addUnique([A,B,B], 6).
A = 0,
B = 3 ;
A = 4,
B = 1 ;
A = 6,
B = 0.

; 列举了变量之间允许的绑定的下一个解决方案。 请注意, AB绝不会根据需要采用相同的值,但是输入列表中所有出现的内容总和为6 另一个查询:

 ?- addUnique([A,A,A],4).
false.

结果是失败的,因为找不到单个整数来绑定到A ,该A加起来总计为4 ,而:

 ?- addUnique([A,A,A,A],4).
A = 1.

...符合预期 另外,您想尝试:

?- addUnique([A,B,C,D],4).
false.

同样,这里的结果是失败的,因为所有变量ABCD都被断言为不同,并且不能全部绑定为1

编辑 PS。 ony也想尝试:

?- addUnique([A,A,A,1],4).
A = 1.

对上述代码的简单修改可确保在调用ins声明域时仅使用变量(而不是输入列表中的任何数字)。

这是我想出的解决方案。 它将只将输入分配为小于10的数字,但效果很好!

addUnique( A, Answer ) :- 
    used(A,[0,1,2,3,4,5,6,7,8,9],_),
    add(A,Answer).

add( [A|B] , Answer ) :-
    ~ Add everything in the list to come up with answer ~.


% ================================
% Ensures that all variables are unique.  
% ================================

% Base case: Assigned variables unique values
used([], Nin, Nin).

% Have already assigned a value to this variable
used([A|B], Nin, Nout) :-
        integer(A),
        helper(B,Nin,Nout).

% Have not assigned a value to this variable yet
% Assign it and remove it from the list.  
used( [A|B] , Nin, Nout) :-
        member(A,Nin),
        delete(Nin,A,Temp),
        helper(B,Temp,Nout).

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM