让我们对数字1,2,3,4进行排列,它只有一个周期。例如,它可以是:2,3,4,1。我想知道如何使用Prolog生成所有这些排列。
我知道如何使用函数选择生成所有排列。
但是我无法提出一个想法,如何只生成一个周期的排列。
也许有人可以给我一个小的提示或提示。
我的评论旨在直接产生单个周期排列,而不是生成所有排列并过滤掉包含单个周期的排列。
我们也许应该澄清一下,经常使用排列的两种表示形式。 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).
您不能使用该函数生成所有排列,并过滤掉不是“单周期排列”的那些吗? (由于我不清楚“单周期置换”是什么,因此恐怕我无法编写该过滤器。)
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]
,第三个和第四个是原始的List
和Permutation
,最后一个是结果(包含循环元素的列表)。
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.
感谢the answer在hardmath中的讨论,我能够理解它的全部内容。
似乎解决方案非常简单,就是用排列置换输入列表的尾部以形成循环描述,然后通过将每个元素与其接下来,对第一个组件进行排序以将第二个组件放入结果列表: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.