【问题标题】:Mutual exclusivity in CLP(FD)CLP(FD)中的互斥性
【发布时间】:2017-11-21 13:16:27
【问题描述】:

我正在使用 clp(fd) 编写一个 Prolog 程序,并且很难实现我想要的约束之一。

输出是一个整数列表(长度取决于程序另一部分的输入),其中有某些预定义的数字对是互斥的,每对中的一个数字必须 在输出中。

一个例子:

输出是一个整数列表,每个整数都在 1 到 10 之间。输出必须包含 3 或 4,但不能同时包含两者。


到目前为止,我有以下限制,以使 3 和 4 不能同时出现在输出中,但它不能确保其中一个 在输出中。

mutual2([A], ME1):-
    (A in 3 #==> ME1) #/\ (#\ A in 4 #<== ME1).
mutual2([A, B| Tail], ME1):-
    (A in 3 #==> ME1) #/\ (#\ A in 4 #<== ME1),
    (B in 3 #==> ME1) #/\ (#\ B in 4 #<== ME1),
    mutual2([B|Tail], ME1).

编辑:
所以运行:

[A,B] ins 2..6, A #< B, mutual2([1,2,B,A,5],M), label([A,B]).

给予:

A = 2,
B = 3,
M = 1 ;
A = 2,
B = 4,
M = 0 ;
A = 2,
B = 5,
M in 0..1 ;
A = 3,
B = 5,
M = 1 ;
A = 4,
B = 5,
M = 0 ;

但我不希望 A=2, B=5, M in 0..1 成为有效输出,因为 AB 都不是 3 或 4。

【问题讨论】:

  • 您是否还打算将序列限制为正好 5 个整数?
  • 不,输出的长度是基于可变输入的。
  • ExprA #&lt;==&gt; #\ ExprB 呢?
  • 首先声明A in 3..4
  • 是的,这特别是关于互斥性并确保一对中的一个值存在。输入类似于:[A,B] ins 2..6,mutual2([1,2,A,B,5],M), label([A,B])。

标签: prolog clpfd


【解决方案1】:

我可能会使用 CLP(FD) 和 DCG 的组合,因为我们正在处理序列。

这是一个识别恰好包含一个 3 或一个 4 的序列的实现:

:- use_module(library(clpfd)).

one_of_3_4 --> no_3_4, [3], no_3_4.
one_of_3_4 --> no_3_4, [4], no_3_4.

no_3_4 --> [].
no_3_4 --> [X], { X in 1..2 \/ 5..9 }.

这会产生这样的结果:

2 ?- phrase(one_of_3_4, L), label(L).
L = [3] ;
L = [3, 1] ;
L = [3, 2] ;
L = [3, 5] ;
L = [3, 6] ;
L = [3, 7] ;
L = [3, 8] ;
L = [3, 9] ;
L = [1, 3] ;
L = [2, 3] ;
L = [5, 3] ;
L = [6, 3] ;
L = [7, 3] ;
L = [8, 3] ;
L = [9, 3] ;
...

这不是对原始问题的完整解决方案,但应该给出如何以透明方式解决它的想法。


如果您不想使用 DCG,这是另一种方法,它首先指定除 3 或 4 之外的单个数字列表,然后在列表中的任何位置插入 3 或 4 (SWI Prolog):
:- use_module(library(clpfd)).

one_of_3_4(L) :-
    length(L1, _),
    L1 ins 1..2 \/ 5..9,
    ( select(3, L, L1)
    ; select(4, L, L1)
    ).

然后可以这样调用:

2 ?- one_of_34(L), label(L).
L = [3] ;
L = [4] ;
L = [3, 1] ;
L = [3, 2] ;
L = [3, 5] ;
L = [3, 6] ;
L = [3, 7] ;
L = [3, 8] ;
L = [3, 9] ;
L = [1, 3] ;
L = [2, 3] ;
L = [5, 3] ;
L = [6, 3] ;
L = [7, 3] ;
L = [8, 3] ;
L = [9, 3] ;
L = [4, 1] ;
L = [4, 2] ;
L = [4, 5] ;
L = [4, 6] ;
L = [4, 7] ;
L = [4, 8] ;
L = [4, 9] ;
L = [1, 4] ;
L = [2, 4] ;
L = [5, 4] ;
L = [6, 4] ;
L = [7, 4] ;
L = [8, 4] ;
L = [9, 4] ;
...


为了响应 cmets 对此答案,您可以创建一个专门适用于 CLP(FD) 场景的非成员谓词:
not_member(_, []).
not_member(X, [Y|T]) :- X #\= Y, not_member(X, T).

或者简而言之,您可以使用maplist/2not_member/2 缩写为:

not_member(X, L) :- maplist(#\=(X), L).

使用not_member/2,这将按预期工作:

mutual(Output, A, B):-
    member(A, Output), not_member(B, Output).
mutual(Output, A, B) :-
    member(B, Output), not_member(A, Output).

查询产生所有结果:

?- [A,B] ins 2..5, A #< B, mutual([A,B,5],3,4), label([A,B]).
A = 3,
B = 5 ;
A = 2,
B = 3 ;
A = 4,
B = 5 ;
A = 2,
B = 4 ;
false.

【讨论】:

  • 我更愿意在 CLPFD 中解决这个问题,不幸的是我从未使用过 DCG。我的问题还指定输出应包含(例如)3 或 4,但 not 两者。
  • 非常感谢。我相信对 member/3 (如下)也可以这样做(和推广),但这不是 clpfd 的方式,它试图具体化和标记变量而不是约束它们的域,从而打破任何额外的约束逻辑。考虑到我整个问题的更大背景,这不合适。 mutual(Output, A, B):- member(A, Output),\+member(B, Output). mutual(Output, A, B):- \+member(A, Output),member(B, Output).
  • 抱歉,成员/2。这些方法都不适用于 CLPFD。
  • @maxdh 我不认为member/2select/3 与CLP(FD)特别相反。它们非常简单,只有列表语法处理和递归。因此,您可以自己编写它们并拥有一个看起来纯粹是 CLP(FD) 的解决方案,或者您可以使用库,它会做同样的事情。
  • member/2select/3 都涉及的比较。 clpfd 的想法是用域来表达一切,对变量及其域施加约束,最后标记变量。这两个谓词都强制变量过早地取值。如果相互排斥是问题的全部,那么这将是一种有效的方法。否则,它会强制在设置所有约束之前进行标记。
猜你喜欢
  • 2013-04-21
  • 2019-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多