【问题标题】:Counting permutations of a list in prolog计算序言中列表的排列
【发布时间】:2011-04-06 19:00:39
【问题描述】:

prolog 2nd edition 的艺术中有一个问题,您应该在其中定义谓词 even_permutation(Xs,Ys) 和类似的奇数排列,当您查询例如 even_permutation([1,2,3],[2 ,3,1]) 和odd_permutation([1,2,3],[2,1,3]) 为真。这些谓词应该能够在随机列表上工作并确定该列表的排列是否在奇数或偶数位置。如图所示,我有我的置换谓词。

permutation([],[]).
permutation(Xs,[Z|Zs]):-select(Z,Xs,Ys), permutation(Ys,Zs).
select(X,[X|Xs],Xs).
select(X,[Y|Ys],[Y|Zs]):- select(X,Ys,Zs).

我有一个想法,我计算列表的排列数并将它们分组为偶数和奇数排列,然后我的查询可以确定排列是奇数还是偶数,但我不知道如何实现它。如果有更好的方法请告诉我。

【问题讨论】:

  • 嗨,Wasswa Samuel。您的主题行是指计数排列,但我认为您误解了偶数和奇数排列之间的区别。这些不是(通常)关于排列的枚举来定义的,而是关于将排列表达为转置的乘积所需的转置的数量(交换两个项目)。这总是可以做到的,实际上不止一种方式,但相同的排列总是有偶数或奇数的转置“因子”。

标签: math prolog


【解决方案1】:

我知道最初的问题是很久以前发布的,但我最近也在解决 Prolog 的艺术 中的一些问题,并考虑了偶数/奇数排列问题几天。我不想打开重复的问题,所以我在这里发布我的解决方案。

书中的问题问:

even_permutation(Xs, Ys)odd_permutation(Xs, Ys) 编写程序找到Ys,分别是偶数和奇数排列, 列表Xs。例如,even_permutation([1,2,3], [2,3,1])odd_permutation([1,2,3], [2,1,3]) 是真的。

所以它需要置换生成器,而不仅仅是验证器。 @hardmath 提供了偶数或奇数排列的正确定义的链接。本书的作者举了两个简单的例子来说明。

对我来说,关键是找出偶数或奇数排列的递归定义。对于所有排列,Prolog 中的经典排列生成器使用以下概念:

  • N+1 个元素的每个排列都是一个列表,表示 N 个元素的排列,其中第 (N+1) 个元素插入到列表中。

谓词selectinsert 用于进行插入。

对于偶数和奇数排列,我考虑过类似的想法:

  • N+1 个元素的每个 even 排列是一个列表,表示 N 个元素与第 (N+1) 个元素的 even 排列插入到列表中的 odd 位置,或表示 N 个元素的 odd 排列的列表,其中第 (N+1) 个元素插入到 甚至在列表中的位置。

  • N+1 个元素的每个 odd 排列是一个列表,表示 N 个元素与第 (N+1) 个元素的 odd 排列插入到列表中的 odd 位置,或者表示 N 个元素的 even 排列的列表,其中第 (N+1) 个元素插入到 甚至在列表中的位置。

合理的是,在奇数位置插入元素表示相对于原始列表的偶数交换(列表的前面,作为第一个位置,不需要交换,所以它是偶数)。类似地,在偶数位置插入元素表示相对于原始列表的奇数交换。

如果我添加空列表是它自己的偶排列的规则,那么我可以定义以下谓词来生成偶排列和奇排列:

even_permutation( [], [] ).
even_permutation( [X|T], Perm ) :-
    even_permutation( T, Perm1 ),
    insert_odd( X, Perm1, Perm ).
even_permutation( [X|T], Perm ) :-
    odd_permutation( T, Perm1 ),
    insert_even( X, Perm1, Perm ).

odd_permutation( [X|T], Perm ) :-
    odd_permutation( T, Perm1 ),
    insert_odd( X, Perm1, Perm ).
odd_permutation( [X|T], Perm ) :-
    even_permutation( T, Perm1 ),
    insert_even( X, Perm1, Perm ).

insert_odd( X, InList, [X|InList] ).
insert_odd( X, [Y,Z|InList], [Y,Z|OutList] ) :-
    insert_odd( X, InList, OutList ).

insert_even( X, [Y|InList], [Y,X|InList] ).
insert_even( X, [Y,Z|InList], [Y,Z|OutList] ) :-
    insert_even( X, InList, OutList ).

【讨论】:

  • @false hehe 从技术上讲,因为我说“超过” 2 年,所以我被覆盖了,但我会调整。谢谢。 :) 顺便说一句,你是怎么找到的?很长一段时间后添加另一个答案不会将其撞到堆的顶部。
  • 如果按活动排序,它确实会增加。 “超过 2 年”确实是一个单调的表达 :-)。
【解决方案2】:

这是关于如何确定排列是偶数还是奇数的高级描述,in the usual sense of the terms

将排列转换为不相交循环的乘积。排列的奇偶性是其因子的奇偶性之和(偶数次偶数或奇数次奇数是偶数,奇数次偶数或偶数次奇数是奇数)。奇数长度的循环是偶数排列,偶数长度的循环是奇数排列。例如,长度为 1 的循环是恒等置换,因此(表示为转置的空乘积)是偶置换。

可以在您的设置中找到不相交循环表示的乘积,方法是从第一个列表中选择一个项目,查找它在第二个列表中对应位置映射的位置,然后使用每个后续列表的图像重复此查找item 直到循环返回到第一个列表开头的 item。连续图像的列表将与不在列表中的项目的轨道不相交,因此认为它们已被消除并开始寻找下一个不相交的循环,其中第一个列表的第一个项目尚未消除。最终,第一个列表中的所有项目都将被淘汰,并被纳入一个或另一个构建的循环中。

上面链接的文章中描述了确定给定排列奇偶性的其他方法。如果您真的希望仅枚举偶数排列(分别是奇数排列),一种方法是修改 the enumeration of all permutations 以仅返回相同奇偶校验的排列(参见该部分和以下部分)。

【讨论】:

    猜你喜欢
    • 2022-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    相关资源
    最近更新 更多