我遇到了以下问题:
从初始集[1,2,3,4]
计算所有可能的子集,即[[1],[2],[3],[4],[1,2],[1,3],[1,4],[2,3],[2,4],[3,4],[1,2,3],[1,2,4],[1,3,4],[2,3,4],[1,2,3,4]]
我编写了以下Haskell程序generate.hs
,这是正确的。
generateSets :: Eq a => [a] -> [[a]] -> [[a]] -> [[a]]
generateSets [] _ _ = []
generateSets src [] _ = let isets = growthup [] src in generateSets src iset iset
generateSets src sets rsets = if null sets' then rsets else generateSets src sets' (rsets++sets')
where sets' = concatMap (flip growthup src) sets
growthup :: (Eq a) => [a] -> [a] -> [[a]]
growthup ps ss = map (\suf -> ps++[suf]) ss'
where ss' = nextoccurence ps ss
nextoccurence :: (Eq a) => [a] -> [a] -> [a]
nextoccurence [] ys = ys
nextoccurence xs ys = tail ys'
where ys' = dropWhile (/= last xs) ys
在GHC解释器中执行它 ghci ...
ghci> generate [1,2,3,4] [] []
ghci> [[1],[2],[3],[4],[1,2],[1,3],[1,4],[2,3],[2,4],[3,4],[1,2,3],[1,2,4],[1,3,4],[2,3,4],[1,2,3,4]]
每件事情都很顺利,但程序花费的时间太长,例如只有30件小套装。
我的问题是:有可能改进我的代码,以便从haskell懒惰,或garbagge收藏家或其他东西中获得更多?
我的代码是否适合并行化?
感谢您的回复!
答案 0 :(得分:7)
集合包含批次子集。实际上,一组 n 元素具有 2 n 子集,因此一组30个元素具有超过十亿个子集。无论您使用哪种方法生成它们,即使迭代结果也需要很长时间。对于较大的套装,你几乎可以忘记在宇宙热死之前经历它们。
因此,只有在性能方面才能做到这一点,因为即使将算法的速度提高一倍,也只能让你同时处理一个元素的列表。 对于大多数应用程序,真正的解决方案是避免首先枚举所有子集。
也就是说,有一种简单的归纳方式来思考子集,这使得定义一个合适的子集函数变得容易,而不必进行任何相等的比较,这解决了实现中的一些问题。
对于基本情况,空集有一个子集:空集。
subsets [] = [[]]
对于具有至少一个元素(x:xs)
的集合,我们有包含该元素的子集,而不包含该元素的子集。我们可以通过递归调用x
来获取不包含subsets xs
的子集,我们可以通过将x
添加到这些子集来获得其余部分。
subsets (x:xs) = subsets xs ++ map (x:) (subsets xs)
Data.List
中subsequences
的定义遵循相同的原则,但采用稍微优化的方式,也以不同的顺序返回子集,更好地利用共享。但是,正如我所说,列举长度为30的列表的子集无论如何都会变慢,最好的办法就是尽量避免在第一时间这样做。