我试图创建谓词example(N, L)
,其中L
是给定N的列表:
此外,在出现数字n之前,我们希望{1,...,n-1}中的每个数字至少出现一次
例如,
`?- example(3, L).
L = [1, 1, 2, 2, 3, 3] ;
L = [1, 1, 2, 3, 3, 2] ;
L = [1, 2, 2, 1, 3, 3] ;
L = [1, 2, 2, 3, 3, 1] ;
L = [1, 2, 3, 3, 2, 1] ;
L = [1, 2, 3, 1, 2, 3] ;`
所以我的想法是创建一个像1_2_3 _... n_这样的列表,填充所有" _"与n大小的集合的排列。对于N = 2,我将得到1 1 2 2,1 2 2 1满足所有条件。我尝试了很多次,但仍然不知道如何在SWI中创建这样的结构。我要感谢任何暗示,提前谢谢。
答案 0 :(得分:1)
为了让您了解如何使用clpfd
库解决此任务,我写了部分解决方案:
:- use_module(library(clpfd)).
gapBetweenElements(_,C,N):-
C > N.
gapBetweenElements(L,C,N):-
C =< N,
N1 #< N2,
N2 - N1 #= T,
T rem 2 #= 1,
element(N1,L,C),
element(N2,L,C),
C1 is C+1,
gapBetweenElements(L,C1,N).
countElement([],_,[]).
countElement([H|T],E,[HB|TB]):-
HB in 0..1,
H #= E #<==> HB #= 1,
countElement(T,E,TB).
occurrencesNumber(_,C,N,_):-
C > N.
occurrencesNumber(L,C,N,Times):-
C =< N,
countElement(L,C,LB),
sum(LB,#=,Times),
C1 is C+1,
occurrencesNumber(L,C1,N,Times).
applyDomain([],_).
applyDomain([H|T],V):-
H in 1..V,
applyDomain(T,V).
example(N,L):-
Len is 2*N,
length(L,Len),
applyDomain(L,N),
occurrencesNumber(L,1,N,2),
gapBetweenElements(L,1,N),
label(L).
使用length/2
约束列表由Len
个元素组成。使用applyDomain/2
,您可以为每个元素设置域名(在这种情况下,从1
到N
)。使用occurrencesNumber/4
约束列表以包含每个元素,恰好两次(注意,为了计算列表中的元素,我使用countElement/3
使用reification变量对每个元素进行计数。这可能不是最佳解决方案,因为物化变量不会传播很多......)。使用gapBetweenElements/3
,您强制要求相同元素之间的差距必须均匀。最后使用label/1
,您可以搜索解决方案。
?- example(3,L).
L = [1, 1, 2, 2, 3, 3]
L = [1, 1, 2, 3, 3, 2]
L = [1, 1, 3, 2, 2, 3]
L = [1, 1, 3, 3, 2, 2]
and so on...
现在你需要实现最后一个约束,如你所说:“在出现数字n之前,我们希望{1,...,n-1}中的每个数字至少出现一次”。