你如何有效地找到haskell中值列表的联合?

时间:2014-09-25 17:31:04

标签: haskell set set-union

由于一个代码示例胜过千言万语,我将从那开始:

testList = [1,2,2,3,4,5]
testSet = map sumMapper $ tails testList
          where sumMapper [] = []
                sumMapper (a:b) = sumMap a b
                sumMap a b = map (+ a) b

此代码采用一个列表并将所有元素相加以获得所有元素的总和(我也对此效率感兴趣)。 testSet的输出是:

[[3,3,4,5,6],[4,5,6,7],[5,6,7],[7,8],[9],[],[]]

我想找到这些列表的联合(使其成为一组)但我觉得:

whatIWant = foldl1 union testSet

会有糟糕的表现(真正的名单会长达数千个元素)。

这是正确的解决方案还是我遗漏了一些明显的东西?

3 个答案:

答案 0 :(得分:5)

如果您使用属于Ord类型类的成员的元素(如示例中所示),则可以使用Data.Set

import qualified Data.Set as Set

whatYouWant = foldl' (Set.union . Set.fromList) Set.empty testSet

Set.fromList . concat解决方案一样,这样做的优点是可以使空间与最大子列表的大小成比例,而不是与整个连接列表的大小成比例。 strict foldl'还可以防止未评估的thunk堆积,防止O(n)堆栈和堆空间使用。

一般来说,Ord约束允许比Eq约束更有效的算法,因为它允许您构建树。这也是nub O(n^2)的原因:效率更高的算法需要Ord,而不仅仅是Eq

答案 1 :(得分:4)

您可能想尝试

nub $ concat theListOfLists

在使用union的版本中,删除重复项的代码将多次运行。这里只运行一次。

它只执行代码以提取一次唯一值。

还有一个Data.Set库,您也可以使用

import Data.Set
S.fromList $ concat theListOfLists

重要的一点是,提取重复项的代码(此处和上方)只能在完整列表中运行一次,而不是一遍又一遍。


编辑 - 在下面提到nub是O(n ^ 2),所以你应该避免上面的第一个解决方案支持O(n log n),因为Data.Set.fromList应该是。正如其他人在评论中提到的那样,你需要一些能够强制Ord a来获得适当复杂性O(n log n)的东西,而Data.Set则需要强制实现复制{{1}}。

我会留下两个解决方案(性能不佳和性能良好),因为我认为最终的讨论很有用。

答案 2 :(得分:1)

由于union是关联操作( a +(b + c)==(a + b)+ c ),因此您可以使用树形折叠来获得对数优势时间复杂度:

_U []     = []
_U (xs:t) = union xs (_U (pairs t))

pairs (xs:ys:t)  = union xs ys : pairs t
pairs t          = t

当然Data.List.union本身就是 O(n 2 ,但是如果你的testList被命令为非递减,则所有列表也是如此,您可以使用线性ordUnion代替union,以获得整体线性的解决方案,不应泄漏空间:

ordUnion :: (Ord a) => [a] -> [a] -> [a]
ordUnion a      []     = a
ordUnion []     b      = b
ordUnion (x:xs) (y:ys) = case compare x y of
   LT -> x : ordUnion xs  (y:ys)
   EQ -> x : ordUnion xs     ys
   GT -> y : ordUnion (x:xs) ys

为了防止可能漏掉的重复项,需要另外一个函数来处理_U的输出 - 线性ordNub :: (Ord a) => [a] -> [a],具有明显的实现。

使用左优先(\(x:xs) ys -> x:ordUnion xs ys)可以提高整体效率(在每个给定时刻强制输入较小的部分):

g testList = ordNub . _U $ [map (+ a) b | (a:b) <- tails testList]
  where
    _U []         = []
    _U ((x:xs):t) = x : ordUnion xs (_U (pairs t))
    pairs ((x:xs):ys:t) = (x : ordUnion xs ys) : pairs t
    pairs t             = t

另见: