Prolog - 3个元素的第N个排列

时间:2013-08-19 04:19:21

标签: prolog permutation

我想获得3个元素的第n个排列。 例如。

列表= [1,2,3] n = 5

输出 [1,2,3,1,2] [1,2,3,2,1] ...

我不希望任何连续的相同元素如 [1,1,1,1,1] [1,2,3,3,2] ...

我尝试了很少可以得到完整的排列,无法想出如何得到我需要的东西。

varia_repp(N,RVaria):-varia_rep(N,[1,2,3],RVaria).

varia_rep(0,_,[]).
varia_rep(N,[H|L],RVaria):-N>0,N1 is N-1,delete(H,[H|L],_),varia_rep(N1,[H|L],RVaria).

perm(List,[H|Perm]):-delete(H,List,Rest),perm(Rest,Perm).
perm([],[]).

2 个答案:

答案 0 :(得分:2)

执行此操作的一种方法(请注意,排列按字典顺序排列)是明确说明两个连续元素不能相同:

perm(1, Input, [Last]) :-
    member(Last, Input).
perm(N, Input, [First,Second|Perm]) :-
    N > 1, N0 is N-1,
    member(First, Input),
    dif(First, Second),
    perm(N0, Input, [Second|Perm]).

?- perm(3,[a,b,c],R).
R = [a, b, a] ;
R = [a, b, c] ;
R = [a, c, a] ;
R = [a, c, b] ;
R = [b, a, b] ;
R = [b, a, c] ;
R = [b, c, a] ;
R = [b, c, b] ;
R = [c, a, b] ;
R = [c, a, c] ;
R = [c, b, a] ;
R = [c, b, c] ;
false.

您已使用swi-prolog标记了该问题。与其他主要的Prolog实现一样,它有一个谓词dif/2。你还可以在StackOverflow上找到关于dif / 2的一些有趣的讨论,有利有弊。总而言之,它构成了一个约束,即它的两个参数无法统一。它可以用于非实例化变量,在这种情况下,可以防止大量不必要的回溯,而无需任何明确的切割或额外的参数。

此外,不推荐使用delete / 3而选择select / 3。但是,这里只需要member / 2(因为你想重用元素)。

修改

正如评论中指出的那样,虽然dif / 2使事情变得更短,但可能会有点令人困惑。这是一个几乎相同的,稍微冗长的版本,没有dif / 2(对于较大的N来说,它的工作速度也快得多)。

p(N, Input, [First|Rest]) :-
    N >= 1, N0 is N-1,
    member(First, Input),
    p(N0, Input, First, Rest).

p(0, _, _, []).
p(N, Input, Prev, [This|Rest]) :-
    N > 0, N0 is N-1,
    member(This, Input), This \= Prev,
    p(N0, Input, This, Rest).

答案 1 :(得分:2)

根据您的样本判断,您不需要K个元素的第n个排列(样本中K = 3),而是N个元素的排列,其中N的列表构建为填充模板列表中的元素,然后过滤以排除连续的相等元素:

varia_rep(N, L, P) :-
  build_rep(L, N, P0),
  permutation(P0, P),
  \+ append(_, [X,X|_], P).

build_rep(L, N, P0) :-
  length(L, K),
  findall(X, (between(1, N, I), J is ((I-1) mod K) + 1, nth1(J, L, X)), P0).

试验:

?- build_rep([1,2,3],5,P).
P = [1, 2, 3, 1, 2].

?- varia_rep(5,[1,2,3],P).
P = [1, 2, 3, 1, 2] ;
P = [1, 2, 3, 2, 1] ;
P = [1, 2, 1, 3, 2] ...