使用Haskell查找甚至排列

时间:2017-07-07 18:52:04

标签: haskell permutation

我试图弄清楚如何在集合{permutations [1..n]}中找到偶数排列。我之前在另一个论坛上问过这个问题,得到的答案就是代码:

Import Data.List

-- number of inversions in a permutation
inversions as = sum $ map go (tails as)
where go [] = 0
go (x:xs) = length $ filter (<x) xs

evenPerm as = even (inversions as)

alternating n = [ p | p <- permutations [1..n], evenPerm p ]

我理解代码中的最后一行:alternating n =[p | p <- permutations [1..n], evenPerm p]。也就是说,集合p的{​​{1}}使它们甚至是排列。我认为函数{permutations [1..n]}也是我理解的。它只是集evenPerm的偶数元素。我真正不明白它是如何工作的是反转作为函数。天真地,我怎么想象事情的工作取决于集合的元素{permutations [1..n]} [1..n]即(1,2,3,..,n)并比较其他所有元素设置到这一个,并计算你必须采取多少动作来获得它,但你如何在Haskell中做到这一点?

2 个答案:

答案 0 :(得分:2)

从鸟瞰角度来看,我们想要做的是计算我们需要对列表进行排序所需的交换次数。 (等效地,在群论的语言中,将置换分解为换位。)我们用来计算掉期的排序算法与正确性无关:偶数置换将产生偶数个互换而奇数置换为奇数不管我们如何分类,都可以进行交换。

让我们先来看看:

go [] = 0
go (x:xs) = length $ filter (<x) xs

该示例中的缩进令人困惑,但goinversions的嵌套本地函数。它出现在where子句中。 go函数计算列表中需要移动之前列表中第一个元素的元素数,即小于列表头部的元素。它通过过滤尾部并取其长度来实现。一个空列表没有头,所以有另一个模式匹配它的完整性。 (否则,编译器会抱怨某些输入与任何模式都不匹配。)

inversions as = sum $ map go (tails as)

tails函数来自我们导入的Data.List库。它会生成一个更短和更短的最终细分列表:tails [1,2,3] = [[1,2,3] [2,3], [3]]。然后,我们将go映射到列表中的每个最终段,为我们提供每个最终段的反转计数列表。最后,我们将计数总结。

您经常会看到这样的内容被写成函数组合:inversions = sum . map go . tails。这只是从右到左应用每个函数:tails,然后是结果的map go,然后是结果sum

例如。

list == [4,3,2,1]
tails list == [[4,3,2,1], [3,2,1], [2,1], [1]]
map go (tails list) == [3,2,1,0]
sum $ map go (tails list) == 6

这会计算冒泡排序的互换次数。理论上我们可以执行以下交换来对列表进行排序:

[4,3,2,1] -- 0 swaps needed to sort [1]
[4,3,1,2] -- 1 swap to sort final sequence [2,1]
[4,1,3,2] -- Which equals go [2,1]
[4,1,2,3] -- 2 additional swaps to sort [3,2,1]
[1,4,2,3] -- Which equals go [3,2,1]
[1,2,4,3]
[1,2,3,4] -- 3 additional swaps to sort [4,3,2,1]
          -- Which equals go [4,3,2,1]

这远远不是最小数量的交换,但我们只需要对某些排序算法进行一些计算,而且这很简单。

下一步是

evenPerm as = even (inversions as)

这是一个谓词,它只是告诉我们我们刚才看到的计算结果是偶数还是奇数。它也可以定义为even . inversionseven . sum . map go . tails

alternating n = [ p | p <- permutations [1..n], evenPerm p ]

这是列表理解。它调用Data.Listpermutations中的另一个函数来生成所有排列的列表。然后,当且仅当evenPerm p为真时,它才会将排列 p 添加到输出列表中。

这也可以写成evenPerms = filter evenPerm . permutations,它更短,适用于更多类型,alternating n = evenPerms [1..n]。也就是说,给定输入列表,生成其排列并对它们应用过滤器。 (此版本的alternating仅适用于数字,因为它使用[1..n],但算法在使用小于运算符的任何内容上都可以正常工作。)

清理版

import Data.List

{- In the type signatures below, capitalized type names are specific types.
 - Lowercase type parameters are generic.  Ord, to the left of the => sign,
 - is the type class of all types with an ordering relation.  So, the argu-
 - ment is a list of some type a that can be ordered, and the return value
 - is a Bool, True or False.
 -}
isEvenPerm :: Ord a => [a] -> Bool
isEvenPerm = even . sum . map go . tails
  where go []     = 0
        go (x:xs) = length . filter (<x) $ xs

evenPerms :: Ord a => [a] -> [[a]]
evenPerms = filter isEvenPerm . permutations

{- This only makes sense for positive whole numbers. -}
alternating :: Integral a => a -> [[a]]
alternating n | n >= 1    = evenPerms [1..n]
              | otherwise = error "Argument must be positive."

您可以尝试evenPerms "abcd"alternating 4

答案 1 :(得分:0)

我知道OP想要一个&#34;赤手空拳&#34;代码,但这是combinat包的一种方式(我是这个包的忠实粉丝),这对未来的读者很有帮助。由于我花了一些时间来实现这一点,我认为值得分享。

import           Data.List hiding (permutations) -- just in case
import           Math.Combinat.Permutations

foo :: [[Double]]
foo =
  [[ permuteList p [phi/2, 1/2, 1/2/phi, 0]  | p <- permutations4, isEvenPermutation p] ++
  [ permuteList p [phi/2, 1/2, -1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p] ++
  [ permuteList p [phi/2, -1/2, 1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p] ++
  [ permuteList p [-phi/2, -1/2, 1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p] ++
  [ permuteList p [-phi/2, -1/2, 1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p] ++
  [ permuteList p [-phi/2, -1/2, -1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p] ++
  [ permuteList p [-phi/2, 1/2, -1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p] ++
  [ permuteList p [phi/2, 1/2, -1/2/phi, 0]  |  p <- permutations4, isEvenPermutation p]]
  where
    permutations4 = permutations 4
    phi = (1 + sqrt 5) / 2