我怎样才能优化这个列表理解?

时间:2012-01-04 16:47:14

标签: optimization haskell profiling

我想从列表中生成所有可能的对,其限制是(a,b)==(b,a)和((a,b),(c,d))= =((c,d),(a,b))对于所有a,b,c,d。另外,我可以假设我作为参数提供的列表中的所有元素都是不同的。

我做的第一件事就是写下这个列表理解:

pairsOfPairs :: [a] -> [((a,a),(a,a))]
pairsOfPairs xs = [((w,x),(y,z)) | w <- xs, x <- xs, y <- xs, z <- xs,
                      w < x, y < z, w < y, w /= z, x /= y, x /= z]

这具有惯用的优点,但速度非常慢(分析显示,在此功能中花费了近90%的运行时间,而另一种类似功能)。

缓慢的原因在于,对于n个元素的列表,它生成n ^ 4个候选对对,但是这些限制最终将其缩减为n!/(8 *(n-4)!),这意味着我们做了至少8次太多的工作。

有没有办法重写能够提高速度的函数pairsOfPairs?显然它仍然是O(n ^ 4),但我希望降低常数。


编辑:实际上,我几乎总是用长度为5的列表调用此函数,这意味着结果中有5个!/ 8 = 15个元素,但该函数会生成一个列表5 ^ 4 = 625个元素作为中间步骤。如果我可以消除所有这些中间元素,那么我将获得大约40倍的加速!

1 个答案:

答案 0 :(得分:8)

减少完成工作量的一种简单方法是尽早过滤。

pairsOfPairs :: Ord a => [a] -> [((a,a),(a,a))]
pairsOfPairs xs = [((w,x),(y,z)) | w <- xs, x <- xs, w < x, y <- xs, w < y, x /= y, 
                                   z <- xs, y < z, w /= z, x /= z]

通过在条件可用时立即检查条件,我们避免每对(w,x)使用x <= w等O(n ^ 2)工作。这不是太糟糕。

但是通过预处理列表可以获得更多,如果它被排序,我们可以通过选择像

这样的对来避免几乎所有不必要的工作
ordPairs :: [a] -> [(a,a)]
ordPairs (x:xs) = [(x,y) | y <- xs] ++ ordPairs xs
ordPairs [] = []