如果我想找到列表的排列,我知道排列的数量由多项系数给出。例如,“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)是否有任何标准库函数可以在“真实”的排列意义上计算列表的排列?
答案 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)
这是一个设计问题,应该深入研究。 permutation
将列表中的元素视为彼此不同。您可以执行permutations [0, 0, 0]
,但您还会获得一份 6 大小的列表。
是的,您拥有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