我试图使用CLP(FD)在BProlog中实现词典排序约束。
据我所知,手册中的BProlog没有提供内置的lexLeq
约束(尽管这种全局约束存在有效的传播算法),所以我试图写我自己的,并将排序表达为以下二元约束集:
X1 #=< Y1, (X1 #= Y1) #=> (X2 #=< Y2), (X1 #= Y1 #/\ X2 #= Y2) #=> (X3 #=< Y3), ..., (X1 #= Y1 #/\ ... #/\ XN #= YN) #=> (XN+1 #=< #YN+1)
要表达(A1 #/\ A2 #/\ ... #/\ AN) => AN+1
约束,我认为我应该能够重新启用Ai
,所以:
domain(B, 0, 1),
(X1 #= Y1) #<=> B
然后我收集B
并检查连词是否有效我只是这样做:
(sum(Bs) #= N) #=> AN+1
这个想法会导致以下(可能非常难看)代码:
lexLeq(Xs, Ys) :-
lexLeq(Xs, [], Ys, []).
lexLeq([X|Xs], [], [Y|Ys], []) :-
X #=< Y,
lexLeq(Xs, [X], Ys, [Y]).
lexLeq([X|Xs], [OldX|OldXs], [Y|Ys], [OldY|OldYs]) :-
makeAndConstr([OldX|OldXs], [OldY|OldYs], X, Y),
lexLeq(Xs, [X,OldX|OldXs], Ys, [Y, OldY|OldYs]).
lexLeq([], _, [], _).
makeAndConstr(Xs, Ys, X, Y) :-
length(Xs, N),
makeAndConstr(Xs, Ys, [], N, X, Y).
makeAndConstr([X|Xs], [Y|Ys], Bs, N, X, Y) :-
domain(B, 0, 1),
(X #= Y) #<=> B,
makeAndConstr(Xs, Ys, [B|Bs], N, X, Y).
makeAndConstr([], [], Bs, N, X, Y) :-
(sum(Bs) #= N) #=> (X #=< Y).
此部分有效:
| ?- domain([A,B,C,D,E,F], 0, 1), lexLeq([A,B,C], [D, E, F]), labeling([A,B,C,$
A = 0
B = 0
C = 0
D = 0
E = 0
F = 0 ?;
A = 0
B = 0
C = 0
D = 1
E = 1
F = 1 ?;
A = 1
B = 1
C = 1
D = 1
E = 1
F = 1 ?;
no
正如您所看到的,所有生成的解决方案都满足约束条件。问题是并非产生所有有效的解决方案。似乎我所描述的约束也以某种方式暗示X1 #>= X2 #>= ... #>= XN
或类似的东西,因此所有变量都是0
或1
,
上述查询还应返回[0,1,0]
vs [0,1,0]
或[0,0,0]
vs [0,1,0]
等解决方案。
那么,我的推理是否有问题,或者上述定义中是否存在错误?
答案 0 :(得分:2)
在makeAndConstr/6
的第一个子句中,您将X
用于两个不同的目的,这会导致额外的失败(Y
相同)。这个重命名的代码有效:
makeAndConstr([X1|Xs], [Y1|Ys], Bs, N, X, Y) :-
domain(B, 0, 1),
(X1 #= Y1) #<=> B,
makeAndConstr(Xs, Ys, [B|Bs], N, X, Y).
您可以通过追踪您希望成功的简单目标来找到这一点,例如: lexLeq([0,1],[0,1])
。
更简单的词典排序约束
我希望与您分享一个优雅的解决方案,这是我多年前由我的前同事Warwick Harvey教授的。它是这样的:
lex_le(Xs, Ys) :-
lex_le(Xs, Ys, 1).
lex_le([], [], 1).
lex_le([X|Xs], [Y|Ys], IsLe) :-
IsLe #<=> (X #< Y+RestIsLe),
lex_le(Xs, Ys, RestIsLe).
通过观察IsLe
为1来解释,如果
X<Y
(以及RestIsLe
的值并不重要)X=Y
和RestIsLe
为1。答案 1 :(得分:1)
好的,我找到了一个看似可行的解决方案:
lexLeq([], []).
lexLeq([X|Xs], [Y|Ys]) :-
X #=< Y,
domain(B, 0, 1),
(X #= Y) #<=> B,
lexLeq(Xs, Ys, [B]).
lexLeq([X|Xs], [Y|Ys], Bs) :-
length(Bs, N),
(sum(Bs) #= N) #=> (X #=< Y),
domain(B, 0, 1),
(X #= Y) #<=> B,
lexLeq(Xs, Ys, [B|Bs]).
lexLeq([], [], _).
这也比上面简单得多。
不同之处在于,在第一个解决方案中,我为每次调用B
创建了新的makeAndConstr
,而不是重复使用已创建的B
。
虽然我不确定这有助于避免这个错误;它应该更有效率。