生成关于属性的N个所有排列(N-给定):每2 <= i <= n存在1 <= j <= i,因此| Prolog中的| v(i)-v(j)| = 1

时间:2019-11-26 06:44:31

标签: prolog

根据属性生成N的所有排列(N-给定):每2 <= i <= n存在1 <= j <= i, 因此| v(i)-v(j)| = 1。我找到了一个解决方案,它建立了前N个数字的列表(将其反向生成),将其反转,测试是否满足条件,然后将每个数字应用于使用候选谓词找到的可能排列。这是到目前为止可以使用的代码:

% candidate returns each number from the list and the list without it
% candidate(L - list, R - integer, L1 - list)
% flow(i,o,o)
% candidate(l1...ln)= - l1,l2...ln
%                     - (e,l1 U L), (e,L) = candidate(l2...ln)

candidate([H|T],H,T).
candidate([H|T],R,[H|T1]):-candidate(T,R,T1).

%build(N - integer, L - list)
%flow(i,o)
%build(l1...ln,n) = - [], n = 0 or 1
%                   - build(l2...ln,n-1), n>1

build(0,[]).
build(1,[]).
build(N1,[N|L]) :- N1 > 1, N is N1 - 1, build(N,L).

%reverse(L - input list, Z - list, R - list)
%flow(i,o,i)
%reverse(l1...ln) = - [], list is empty
%                   - l1 U reverse(l2...ln)
reverse([],Z,Z).
reverse([H|T],Z,Acc) :- reverse(T,Z,[H|Acc]).

%buildReverse(N - integer, R - list)
%flow(i,o)
buildReverse(N1,R):-build(N1+1,R1),reverse(R1,R,[]).

%checks whether the condition in the requirement
%nrTest(L - list, R - int, I - int)
%nrTest(i,i,o)
%nrTest(l1...ln,r,i) = - false, l1 = r, i = 1
%                      - true, abs(l1-r)= 1, list length is 1
%                      - true, abs(l1-r)= 1
%                      - nrTest(l2...ln,r,1)
%                      - nrTest(l2...ln,r,1), i = 0, l1 = r
nrTest([],0,_).
nrTest([R|T],R,0):-nrTest(T,R,1).
nrTest([R|_T],R,1):-!,fail.
nrTest([H],R,_):-1 is abs(H-R),true,!.
nrTest([H|_T],R,_):-1 is abs(H-R),true,!.
nrTest([_H|T],R,_I):-nrTest(T,R,1).

% checks if for every element of the list the condition in the
% requirement is met
% test2(L - list, R - list)
% flow(i,i)
% test2(l1...ln,r1...rn) = - false, lists are empty
%                          - true, first list is empty
%                          - true, lists have one element each
%                          - test2(l2...ln,r1...rn), if
%                          nrTest(r1...rn,l1,0)
test2([],[]):-false.
test2([],_):-true.
test2([_],[_]):-true.
test2([H|T],R):-nrTest(R,H,0),test2(T,R),!.

%wrapper for test2
%test2Wrap(R - list)
%flow(i)
test2Wrap([]):-false.
test2Wrap([_]):-true.
test2Wrap(R):-test2(R,R).

%perm2(L - list, R - list)
%flow(i,o)
%perm2(l1...ln,r1...rn) = - [], list is empty
%                         - perm2(candidate(l1...ln,r1),r2...rn),
%                         otherwise
perm2([],[]).
perm2(L,[E|T]):-candidate(L,E,R),perm2(R,T).

%perm3(L - list, R - list)
%flow(i,o)
%perm3(l1...ln,r1...rn) = - perm3(l1...ln,r1...rn), test2Wrap(r1...rn)
perm3(L,R):-perm2(L,R),test2Wrap(R).

%wrapper(N - int, R - list)
%flow(i,o)
wrapper(0,[]).
wrapper(N,R):-buildReverse(N,R1),perm3(R1,R).

我很想知道是否有任何更简单的方法可以做到这一点,因为看来我比应该做的要复杂得多。

1 个答案:

答案 0 :(得分:0)

好吧,我们来比较一下permutation/2的SWI-Prolog实现(单击see the source:-按钮。有很多类型检查,然后解析为对此的调用谓词:

perm([], []).
perm(List, [First|Perm]) :-
    select(First, List, Rest),
    perm(Rest, Perm).

这看起来与您没什么不同;让我们看看select/3candidate/3是否具有可比性:

select(X, [Head|Tail], Rest) :-
   select3_(Tail, Head, X, Rest).

select3_(Tail, Head, Head, Tail).
select3_([Head2|Tail], Head, X, [Head|Rest]) :-
    select3_(Tail, Head2, X, Rest).

因此,文档建议此处使用select3_/3的目的实际上是使谓词在最后一个元素上具有确定性。实际上,除了参数的顺序之外,candidate/3select/3之间的区别很小:

?- candidate([a,b,c], X, Y).
X = a,
Y = [b, c] ;
X = b,
Y = [a, c] ;
X = c,
Y = [a, b] ;
false.

?- select(X, [a,b,c], Y).
X = a,
Y = [b, c] ;
X = b,
Y = [a, c] ;
X = c,
Y = [a, b].

此处没有结尾false

有一个内置reverse/2,因此不需要您编写代码。没有内置谓词可为您提供长度为N的整数列表。如果不使用reverse/2,我的编码可能会略有不同,就像这样:

iota(N, L) :- iota(1, N, L).

iota(N, N, [N]).
iota(N0, N, [N0|NL]) :- 
    N0 < N, 
    succ(N0, N1), 
    iota(N1, N, NL).

过去,我们进入了您的wrapper/2perm3/2test2/2nrTest/3谓词,但我不太了解您在做什么。但是我认为置换生成器本质上没有什么可怕的,您只需要利用它们存在的库例程即可(删除candidate/3并使用select/3,使用提供的reverse/2真的有必要)。

Prolog的水平很高,但不是很简洁。高质量的Prolog代码 可以比同类过程代码短,但是(以我的经验)这与您可以使用的各种调用约定和关系思维有关,因为该语言本身并不是没那么短。 I wrote about this on my blog,并以another question为例。