Prolog 单圈置换

Prolog 单圈置换,prolog,permutation,Prolog,Permutation,让我们看一个数{1,2,3,4}的排列,其中只有一个圈。例如,它可以是:(2,3,4,1)。我想知道,如何使用Prolog生成所有这样的排列 我知道如何使用select生成所有排列 但是我想不出如何只生成一个循环(即单循环)置换 有人能给我一点提示或建议吗?你能不能用这个函数来生成所有排列,并过滤掉那些不是“单周期排列”的排列?(由于我根本不清楚什么是‘单周期排列’,恐怕我无法帮助编写该过滤器。) cycle/5谓词构建与您传递的第一个参数对应的循环。第二个参数是一个累加器,初始化为[First

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

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

但是我想不出如何只生成一个循环(即单循环)置换


有人能给我一点提示或建议吗?

你能不能用这个函数来生成所有排列,并过滤掉那些不是“单周期排列”的排列?(由于我根本不清楚什么是‘单周期排列’,恐怕我无法帮助编写该过滤器。)

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

的调用对应于/4
检索替代排列中第一个参数的项:

    corresponds(Current, List, Permutation, R),
如果这个项目在我们正在构建的周期中,这意味着我们已经完成了周期的构建,所以我们统一了
周期
和累加器(
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.

我的评论意在暗示直接生成单循环置换,而不是生成所有置换并过滤掉由单循环组成的置换

我们或许应该澄清,排列的两种表示法经常被使用。xyz写道“我知道如何生成所有置换”,大概意思是类似于我给出的代码。在这里,所有的排列都是按照列表在一些“标准顺序”列表中重新排列项目的方式来表示的

很明显,有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在另一个方向解决了转换问题:给定一个置换as列表,找出该置换中包含的一个循环(并查看它是否为全长)。]

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


多亏了在中的讨论,我才能够理解这一切

似乎解决方案很简单,用排列替换输入列表的尾部,形成循环描述,然后将每个元素与其下一个元素进行配对,并对第一个组件进行排序,从而将第二个组件的列表作为结果列表,从而将其转换为列表表示:

单循环置换([A | B],R):-
置换(B,P),
循环对(A,A,P,CP),
排序(CP、SCP),
映射列表(成对,SCP,R)。
成对(X-Y,X,Y)。
循环u对(A,X,[Y|Z],[X-Y|W]):-
循环_对(A,Y,Z,W)。
循环对(A,X,[],[X-A])。
为了更容易地查看循环,只需删除单循环排列中的最后一个目标即可:

单循环对([A | B],SCP):-
置换(B,P),
循环对(A,A,P,CP),
排序(CP、SCP)。
测试:

21?-forall(单循环对([1,2,3,4],SCP),
(映射列表(成对,SCP,Ur),写入((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]
对。
另见:


关于循环以及一个循环的含义您可以在这里阅读:关于算法的一些内容:我们想要找到一个循环置换。我们有两个列表L和P,P是L的置换,只有一个圈。首先,我们检查是否为真:置换(L,P)。然后我们可以看到,L的头和P的头不应该是相同的数字。接下来,让我们看[1,2,3,4]和[4,1,2,3],如果我们从L和P中选择head,我们注意到2,3,4不能是[1,2,3]的置换(因为一个循环的定义)。我添加了我认为正确的解决方案,因为现在我了解了循环是什么(最后:()我基本上只构建了一个循环,如果这个循环不是列表的大小,那么这个置换就不是一个循环。你怎么看?它构建了一个循环,这样我们就可以将它的长度与列表进行比较,因为如果这个置换是一个单循环置换,它的任何一个循环(只有一个偶数)与列表长度相同…给定n个符号,有(n-1)!这些符号的单圈排列。为什么?请阅读第一类斯特林数,它计算具有k个不相交循环的n个元素的排列数。您应该看到递归,这应该会对您有所帮助,或者我可以向您解释请不要从堆栈溢出中删除您的问题。
    (   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.
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).