Haskell的“排列”函数定义奇怪

时间:2015-08-13 11:54:01

标签: haskell

如果我想找到列表的排列,我知道排列的数量由多项系数给出。例如,“MISSISSIPPI”有11个字母,'S'出现4次,'I'出现4次,'P'出现两次,'M'出现一次。所以“MISSISSIPPI”的排列数等于11!/(4!4!2!)= 34650。

如果我加载GHCi并写:

ghci> import Data.List
ghci> permutations [1,2,3]

它会返回

[[1,2,3],[2,1,3],[3,2,1],[2,3,1],[3,1,2],[1,3,2]]

正如所料。

但如果我写

ghci> permutations [1,0,0]

它现在将返回

[[1,0,0],[0,1,0],[0,0,1],[0,0,1],[0,1,0],[1,0,0]]

......这非常令人失望。由于有三个元素,其中两个出现两次,一个人希望只有6!/ 2! = 3个排列,即

[[1,0,0],[0,1,0],[0,0,1]]

而不是通过将列表中的每个元素视为不同而生成的六个。

1)为什么Haskell以上述方式实现“排列”(即将列表的所有元素视为不同?)

2)是否有任何标准库函数可以在“真实”的排列意义上计算列表的排列?

4 个答案:

答案 0 :(得分:8)

请记住,permutations的类型为

permutations :: [a] -> [[a]]

这意味着它满足自由定理

permutations . map f = (map . map) f . permutations

用于所有函数f。由于您可以任意更改参数列表的元素而不影响结果列表的结构,因此permutations必须真正是原始索引的函数列表,而不是元素。

那么permutations真正在做什么---它必须做什么---计算参数列表的索引的所有排列,然后将每个排列应用于列出并返回结果。 (即,

permutations xn = (map . map) (xn!!) (permutations [0..length xn - 1])

for finite xn)。

数学附录:

由于

xn = map (xn!!) (zipWith const [0..] xn)

对于所有 xn,任何permutations类型的函数都必须满足

permutations xn
  = permutations (map (xn!!) (zipWith const [0..] xn)
  = (map . map) (xn!!) (permutations (zipWith const [0..] xn))

通过上面的xn等式和permutations的自由定理。因此任何具有permutations类型的函数必须只对输入列表[1]的索引起作用。

[1]从技术上讲,您可以使用seq来违反此规定。但仅适用于包含undefined作为元素的输入列表,在您的情况下不是这样。

答案 1 :(得分:7)

1 - 为什么Haskell实现"排列"以上述方式(即将列表的所有元素视为不同?)

这是一个设计问题,应该深入研究。 permutation将列表中的元素视为彼此不同。您可以执行permutations [0, 0, 0],但您还会获得一份 6 大小的列表。

2 - 是否有任何标准库函数可以计算" true"中的列表的排列。排列感?

是的,您拥有Math.Combinat.Permutations,但您可以轻松创建自己的定义,使用集合过滤复杂度为O(n * log n)的唯一组合,并考虑到nub已知很慢:

module Main where
import Data.List (permutations)
import qualified Data.Set as Set

nubOrd :: (Ord a) => [a] -> [a]
nubOrd xs = go Set.empty xs where
  go s (x:xs)
   | x `Set.member` s = go s xs
   | otherwise        = x : go (Set.insert x s) xs
  go _ _              = []

permutations' :: (Ord a) => [a] -> [[a]]
permutations' = nubOrd . permutations

permutations' [1, 0, 0]给出[[1, 0, 0], [0, 1, 0], [0, 0, 1]]

答案 2 :(得分:1)

  

为什么Haskell以上述方式实现“排列”(即将列表的所有元素视为不同?)

因为否则,类型必须是:

permutations :: Eq a => [a] -> [[a]]

然后我们只能置换具有Eq实例的东西。但我记得我有类似

的东西
permutations [(+), subtract, (*), (/)]

在一些Project Euler代码中....

答案 3 :(得分:0)

这是一个稍微重新排列的Daniel Fischer solution

inserts :: [a] -> [a] -> [[a]]
inserts (x:xs) (y:ys) = map (x:) (inserts xs (y:ys)) ++ map (y:) (inserts (x:xs) ys)
inserts  xs     ys    = [xs ++ ys]

uniqPerms :: Ord a => [a] -> [[a]]
uniqPerms = foldM inserts [] . group . sort