Algorithm 推广组合函数?

Algorithm 推广组合函数?,algorithm,haskell,functional-programming,Algorithm,Haskell,Functional Programming,我在Haskell上解决了几个组合问题,所以我写下了这两个函数: permutations :: (Eq a) => [a] -> [[a]] permutations [] = [[]] permutations list = do x <- list xs <- permutations (filter (/= x) list) return (x : xs) combinations :: (Eq a, Ord a) => Int

我在Haskell上解决了几个组合问题,所以我写下了这两个函数:

permutations :: (Eq a) => [a] -> [[a]]
permutations [] = [[]]
permutations list = do
    x  <- list
    xs <- permutations (filter (/= x) list)
    return (x : xs)

combinations :: (Eq a, Ord a) => Int -> [a] -> [[a]]
combinations 0 _ = [[]]
combinations n list = do
    x  <- list
    xs <- combinations (n-1) (filter (> x) list)
    return (x : xs)
这些都是令人不安的相似之处,所以我不得不把它抽象出来。我写了以下摘要:

combinatoric next [] = [[]]
combinatoric next list = do
    x  <- list
    xs <- combinatoric next (next x list)
    return (x : xs)

但是我不能这样定义组合,因为它带有一个状态(
n
)。我可以用一个附加的状态参数扩展组合的,但是那太笨重了,我记得这种方法是不必要的。因此,我想知道:是否有可能使用组合数学定义组合?如果不是,那么什么是更好的组合数学抽象,它成功地包含了这两个函数?

这不是对你问题的直接回答(对不起),但我认为你的代码不正确。
Eq
Ord
约束提示了我——它们不应该是必需的——因此我编写了几个属性

prop_numberOfPermutations xs = length (permutations xs) === factorial (length xs)
    where _ = (xs :: [Int])  -- force xs to be instantiated to [Int]

prop_numberOfCombinations (Positive n) (NonEmpty xs) = n <= length xs ==>
        length (combinations n xs) === choose (length xs) n
    where _ = (xs :: [Int])


factorial :: Int -> Int
factorial x = foldr (*) 1 [1..x]

choose :: Int -> Int -> Int
choose n 0 = 1
choose 0 r = 0
choose n r = choose (n-1) (r-1) * n `div` r

当输入列表包含重复元素时,函数似乎失败。为一个不正确的实现编写一个抽象不是一个好主意——在你能走之前不要尝试运行!您可能会发现阅读标准库的定义很有帮助,它没有
Eq
约束。

首先,让我们改进原始函数。您假设所有元素都是不同的,因为它们在
排列中相等,并且它们是不同的,并且对
组合具有顺序。这些约束不是必需的,正如另一个答案中所描述的,代码可能会产生错误的结果。接下来,让我们只接受无约束列表。为此,我们需要一个helper函数来生成列表的所有可能拆分:

split :: [a] -> [([a], a, [a])]
split = loop []
  where
    loop _  []      = []
    loop rs (x:xs)  = (rs, x, xs) : loop (x:rs) xs
请注意,该实现会使此函数返回的前缀反转,但我们不需要这样做

这允许我们编写通用的
排列
组合

permutations :: [a] -> [[a]]
permutations [] = [[]]
permutations list = do
    (pre, x, post) <- split list
    -- reversing 'pre' isn't really necessary, but makes the output
    -- order natural
    xs <- permutations (reverse pre ++ post)
    return (x : xs)

combinations :: Int -> [a] -> [[a]]
combinations 0 _ = [[]]
combinations n list = do
    (_, x, post) <- split list
    xs <- combinations (n-1) post
    return (x : xs)
然后,通过指定适当的重复次数和核心
StateT[a][]a
函数来定义
排列
组合

import Control.Monad.State

combinatoric :: Int -> StateT [a] [] b -> [a] -> [[b]]
combinatoric n k = evalStateT $ replicateM n k
permutations' :: [a] -> [[a]]
permutations' xs = combinatoric (length xs) f xs
  where
    f = StateT $ map (\(pre, x, post) -> (x, reverse pre ++ post)) . split


combinations' :: Int -> [a] -> [[a]]
combinations' n xs = combinatoric n f xs
  where
    f = StateT $ map (\(_, x, post) -> (x, post)) . split

如果您不介意的话,您能解释一下
(过滤器(/=x)列表)
是如何工作的吗?我看到
x
list
(根据上一行)您正试图从
list
中筛选不等于
list
的元素。
置换
组合
的良好实现可能不需要
Eq
Ord
实例,除非您试图做一些更复杂的事情,以便
置换[1,1]=[[1,1]]
@thefourtheye no,这里
x
只是列表中的一个元素(
1
2
3
…)。这实际上是一种从列表中删除元素的肮脏且错误的方法,即
过滤器(/=2)[1,2,3,4]=[1,3,4]
。我相信还有更好的选择。注意列表的monad实例是这样实现的,
x@Viclib所以,
x@thefourtheye是的,列表monad就是这样做的。在ghci上尝试此操作:
do{x没有问题!但是我在原始线程下面发表了评论,列表不能包含重复的元素。不过,你是对的:我应该使用一个集合来更清楚地说明这一点。谢谢你的关注!无论如何,如果有人好奇,为了在我的代码上解决这个问题,而不是过滤,只需按其索引删除元素。例如,可以使用wr这是一个集合的monad实例,它不仅提供了元素,还提供了列表的“其余部分”。我认为这样的monad不存在
permutations :: [a] -> [[a]]
permutations [] = [[]]
permutations list = do
    (pre, x, post) <- split list
    -- reversing 'pre' isn't really necessary, but makes the output
    -- order natural
    xs <- permutations (reverse pre ++ post)
    return (x : xs)

combinations :: Int -> [a] -> [[a]]
combinations 0 _ = [[]]
combinations n list = do
    (_, x, post) <- split list
    xs <- combinations (n-1) post
    return (x : xs)
import Control.Monad.State

combinatoric :: Int -> StateT [a] [] b -> [a] -> [[b]]
combinatoric n k = evalStateT $ replicateM n k
permutations' :: [a] -> [[a]]
permutations' xs = combinatoric (length xs) f xs
  where
    f = StateT $ map (\(pre, x, post) -> (x, reverse pre ++ post)) . split


combinations' :: Int -> [a] -> [[a]]
combinations' n xs = combinatoric n f xs
  where
    f = StateT $ map (\(_, x, post) -> (x, post)) . split