我想编写一个函数来计算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),...]
答案 0 :(得分:9)
您可以使用(\\)
与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
不同,依此类推。
答案 1 :(得分:8)
我们可以通过answer来优化kuoytfouy中的代码,就像
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
在“唯一选择”单例中choose
-(或等效为picks
-)的Haskell代码严重怀疑效率极低(对于初学者,在完全强制时inits
是二次幂的)。< br />
每次都重新计算缩小的域,就像在此答案中一样,我们在每个时间点仅得到七个(六个,无论如何)域列表,用- but ,每次delete
调用都从头开始搜索 参数(发明了效率低的picks
来解决...),再次表明总体计算是二次的,效率低的。值得深思!答案 2 :(得分:6)
那又怎么样呢?
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]==[]]
答案 3 :(得分:5)
我们可以在此处创建一个“辅助函数”,对于给定列表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), ...