【问题标题】:Finding combinations寻找组合
【发布时间】:2019-09-29 09:57:45
【问题描述】:

我想编写一个函数来计算 7 元组中数字 1 到 7 的所有组合,但每个数字在每个元组中只能出现一次。

到目前为止,我找到了这种方法,但它也会返回每个元组中多次出现相同数字的组合。我不太确定如何删除具有多个的元组 出现相同的次数。

  a = [(a,b,c,d,e,f,g) | a <- [1..7], b <- [1..7], c <- [1..7], 
        d <- [1..7], e <- [1..7], f <- [1..7], g <- [1..7]]

目标结果示例(所有有效组合都应在此处):

  [(1,2,3,4,5,6,7),(2,1,3,4,5,6,7),(2,3,1,4,5,6,7),...]

【问题讨论】:

  • 你可以使用来自Data.List的permutations [1..7]

标签: haskell list-comprehension


【解决方案1】:

您可以使用(\\) 与Data.List 的列表差异。

perms = [ (a,b,c,d,e,f,g) | a <- [1..7]
                          , b <- [1..7] \\ [a]
                          , c <- [1..7] \\ [a,b]
                          , d <- [1..7] \\ [a,b,c]
                          , e <- [1..7] \\ [a,b,c,d]
                          , f <- [1..7] \\ [a,b,c,d,e]
                          , g <- [1..7] \\ [a,b,c,d,e,f] ]

这样b将被选择为不同于a,c将不同于a和b,等等。

【讨论】:

    【解决方案2】:

    我们可以通过kuoytfouy优化answer中的代码,如

    perms = [(a,b,c,d,e,f,g) | a <- [1..7], let dom6 = [1..7] \\ [a]
                             , b <- dom6,   let dom5 = dom6 \\ [b]
                             , c <- dom5,   let dom4 = dom5 \\ [c]
                             , d <- dom4,   let dom3 = dom4 \\ [d] 
                             , e <- dom3,   let dom2 = dom3 \\ [e] 
                             , f <- dom2,   let dom1 = dom2 \\ [f] 
                             , g <- dom1,   let dom0 = dom1 \\ [g]  ]
    

    并通过减少冗余计算进一步改进它,

    perms = [(a,b,c,d,e,f,g) | a <- [1..7], let dom6 = delete a [1..7]
                             , b <- dom6,   let dom5 = delete b dom6
                             , c <- dom5,   let dom4 = delete c dom5
                             , d <- dom4,   let dom3 = delete d dom4 
                             , e <- dom3,   let dom2 = delete e dom3 
                             , f <- dom2,   let [g]  = delete f dom2 ]
    

    将一个元素的选择与从当前域中删除它组合起来,为我们提供了一个同时完成这两项工作的函数,通常称为picks。它过去曾在 SO 答案中使用过,可以在此处找到。

    另见:

    • picks 从一个pigworker
    • choose 在唯一选择单子中
    • 我的Common Lisp answer 有一个高效 代码,它实际上通过对列表结构进行手术突变来缩小域列表,当我们沿着递归构建的嵌套循环向下移动时,将元素一一取出;并在回来的路上治愈它。
      也就是说,基于choose-(或等效地,picks-)的 Haskell 代码被严重怀疑效率极低(对于初学者来说,inits 在完全强制时是二次方)。
      每次重新计算缩小的域,就像在这个答案中一样,我们在每个时间点只得到七个(六个,无论如何)域列表,每个完成后都可以完全垃圾回收 -- 但是 ,每个delete 调用从头开始搜索它的 参数(picks 的低效率被发明来修复......),再次表明整体计算是二次的、低效率的。值得深思!

    【讨论】:

      【解决方案3】:

      类似的东西呢:

      import Data.List
      list = [(a,b,c,d,e,f,g) | a <- [1..7], b <- [1..7], c <- [1..7],
              d <- [1..7], e <- [1..7], f <- [1..7], g <- [1..7], [1,2,3,4,5,6,7]\\[a,b,c,d,e,f,g]==[]]
      

      【讨论】:

        【解决方案4】:

        我们可以在这里创建一个“辅助函数”,为给定的列表xs 生成一个元组列表,其中第一个元素是我们选择的元素,第二个是剩余元素的列表,例如:

        import Data.List(inits, tails)
        
        pick :: [a] -> [(a, [a])]
        pick ls = [(b, as ++ bs) | (as, (b:bs)) <- zip (inits ls) (tails ls)]

        例如:

        Prelude Data.List> pick [1..5]
        [(1,[2,3,4,5]),(2,[1,3,4,5]),(3,[1,2,4,5]),(4,[1,2,3,5]),(5,[1,2,3,4])]
        

        因此,每个项目从列表中选择一个元素并返回一个列表,其中删除了该选择的元素。我们可以使用它将该列表传递给下一个生成器。

        然后我们可以在 do 块中使用它,例如:

        perms :: (Num a, Enum a) => [(a, a, a, a, a, a, a)]
        perms = do
            (a, as) <- pick [1..7]
            (b, bs) <- pick as
            (c, cs) <- pick bs
            (d, ds) <- pick cs
            (e, es) <- pick ds
            (f, [g]) <- pick es
            return (a, b, c, d, e, f, g)
        

        产生:

        Prelude Data.List> perms
        [(1,2,3,4,5,6,7),(1,2,3,4,5,7,6),(1,2,3,4,6,5,7),(1,2,3,4,6,7,5),(1,2,3,4,7,5,6),(1,2,3,4,7,6,5),(1,2,3,5,4,6,7),(1,2,3,5,4,7,6), ...
        

        【讨论】:

        • 我几乎同时发布了类似的内容...您可以使用 (f,[g]) &lt;- pick es 稍微改进您的代码。
        • @WillNess: 是的,尽管用例可能会更改为“例如从[1..10] 中挑选 7 个元素,在这种情况下上述操作将失败。
        • 我有一个关于这类东西的旧答案。搜索“欧拉 43 单子”。稍后会找到链接。 Here it is。有了那个 monad,perms 应该变成 replicateM 7 choose 什么的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多