单周期置换

问题描述 投票:3回答:4

让我们对数字{1,2,3,4}进行排列,其中只有一个循环。例如,它可以是:(2,3,4,1)。我想知道如何使用Prolog生成所有这样的排列?

我知道如何使用select生成所有排列。

但是我无法提出仅生成一个周期(即单个周期)排列的想法。

有人可以给我一个小的提示或建议吗?

prolog permutation
4个回答
1
投票

我的评论旨在直接产生单个周期排列,而不是生成所有排列并过滤掉包含单个周期的排列。

我们也许应该澄清一下,经常使用排列的两种表示形式。 xyz写道“我知道如何生成所有排列”,大概意味着类似我在this 2006 forum post中给出的代码。这里,所有排列都是根据列表以某种“标准顺序”列表重新排列项目的方式表示的。

显然有N!各种排列。其中有多少个是单周期排列?通过考虑对置换有用的另一种形式(即不相交循环的产物),可以轻松地回答该问题。我们需要区分像(1,2,3,4)这样的循环和身份置换[1,2,3,4]。实际上,周期(1,2,3,4)将1映射为2、2映射为3、3映射为4和4映射为1,因此与其在身份置换中不是[2,3,4,1]其列表表示形式。

现在,循环会自行循环,因此选择开始循环表示法是任意的。例如,如果我们从1开始,则循环由以下N-1个项目的顺序确定。这表明有(N-1)个!组成单个循环的N个事物的排列(长度为N)。因此,我们可以很容易地以周期形式生成所有单个周期排列,然后问题就减少到从该周期形式转换为排列的列表形式。 [请注意,Mog在某种程度上解决了转换的另一个方向:给定一个置换列表,找出该置换中包含的一个循环(并查看其是否完整)。]

这是我的代码,用于生成给定“标准订单”列表的所有单周期列表排列,oneCycle(Identity,Permuted)

oneCycle([H|T],P) :-
    permute(T,S),
    oneCycle2permute([H|S],[H|T],P).

permute([ ],[ ]) :- !.
permute(L,[H|T]) :-
    omit(H,L,Z),
    permute(Z,T).

omit(H,[H|T],T).
omit(X,[H|T],[H|Z]) :-
    omit(X,T,Z).

oneCycle2permute(_,[ ],[ ]) :- !.
oneCycle2permute(C,[I|Is],[P|Ps]) :-
    mapCycle(C,I,P),
    oneCycle2permute(C,Is,Ps).

mapCycle([X],X,X) :- !.
mapCycle([H|T],X,Y) :-
    mapCycleAux(H,T,X,Y).

mapCycleAux(Y,[X],X,Y) :- !.
mapCycleAux(X,[Y|_],X,Y) :- !.
mapCycleAux(_,[X,Y|_],X,Y) :- !.
mapCycleAux(H,[_|T],X,Y) :-
    mapCycleAux(H,T,X,Y).

0
投票

您不能使用该函数生成所有排列,并过滤掉不是“单周期排列”的那些吗? (由于我不清楚“单周期置换”是什么,因此恐怕我无法编写该过滤器。)


0
投票
one-cycle([H|T], Permutation) :-
    permutation([H|T], Permutation),
    cycle(H, [H], [H|T], Permutation, Cycle),
    length(Cycle, CycleLength),
    length([H|T], ListLength),
    CycleLength =:= ListLength.

cycle/5谓词构建与您传递的第一个参数相对应的循环。第二个参数是一个累加器,初始化为[FirstArgument],第三个和第四个是原始的ListPermutation,最后一个是结果(包含循环元素的列表)。

cycle(Current, Acc, List, Permutation, Cycle) :-

corresponds/4的调用检索在置换中代替第一个参数的项目:

    corresponds(Current, List, Permutation, R),

[如果此项目在我们正在构建的循环中,则意味着我们已经完成了构建循环,因此我们将Cycle和累加器(Acc)统一。

    (   member(R, Acc)
     -> Cycle = Acc

[否则,我们通过递归调用谓词并使用找到的对应项,然后将其添加到累加器中,这样我们的构建周期便可以保存它:

     ;  cycle(R, [R|Acc], List, Permutation, Cycle)).

corresponds(N, [N|_], [R|_], R) :-
    !.
corresponds(N, [_|L], [_|P], R) :-
    corresponds(N, L, P, R).

用法:

?- one-cycle([1, 2, 3, 4], P).
P = [2, 3, 4, 1] ;
P = [3, 1, 4, 2] ;
P = [3, 4, 2, 1] ;
P = [2, 4, 1, 3] ;
P = [4, 1, 2, 3] ;
P = [4, 3, 1, 2] ;
false.

0
投票

感谢the answerhardmath中的讨论,我能够理解它的全部内容。

似乎解决方案非常简单,就是用排列置换输入列表的尾部以形成循环描述,然后通过将每个元素与其接下来,对第一个组件进行排序以将第二个组件放入结果列表:single_cycled_permutation([A|B], R) :- permutation(B, P), cycle_pairs(A, A, P, CP), sort( CP, SCP), maplist( pair, SCP, _, R). pair( X-Y, X, Y). cycle_pairs( A, X, [Y|Z], [X-Y|W]) :- cycle_pairs(A, Y, Z , W ). cycle_pairs( A, X, [ ], [X-A] ). 为了更轻松地查看周期,只需删除single_cycled_permutation中的最后一个目标:

single_cycled_pairs([A|B], SCP) :-
  permutation(B,    P),
  cycle_pairs(A, A, P, CP),
  sort(                CP, SCP).

测试:

21 ?- forall( single_cycled_pairs([1,2,3,4], SCP), (maplist(pair,SCP,_,R), write((SCP,R)), nl)). [1-2,2-3,3-4,4-1],[2,3,4,1] [1-2,2-4,3-1,4-3],[2,4,1,3] [1-3,2-4,3-2,4-1],[3,4,2,1] [1-3,2-1,3-4,4-2],[3,1,4,2] [1-4,2-3,3-1,4-2],[4,3,1,2] [1-4,2-1,3-2,4-3],[4,1,2,3] true.

另请参见:

Cyclic permutation

© www.soinside.com 2019 - 2024. All rights reserved.