我想从列表中生成所有可能的对,其限制是(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倍的加速!
答案 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 [] = []