创建一个返回符合特定条件的列表的谓词

时间:2018-03-11 20:57:58

标签: list prolog

我试图创建谓词example(N, L),其中L是给定N的列表:

  • 长度为2 * N
  • 从1到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中创建这样的结构。我要感谢任何暗示,提前谢谢。

1 个答案:

答案 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,您可以为每个元素设置域名(在这种情况下,从1N)。使用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}中的每个数字至少出现一次”。