[英]Efficient alternatives to qualified Haskell list comprehensions
As an illustration of qualified list comprehensions in Haskell, the Learn You a Haskell tutorial provides an example of list comprehension that suggests a general approach to finding the right triangles with a given perimeter p
, expressed as a tuple: 作为Haskell中合格列表理解的说明, Learn a Haskell教程提供了一个列表理解的示例,该示例提出了一种通用方法来查找具有给定周长p
直角三角形,表示为元组:
λ> let rightTriangles p = [ (a,b,c) | c <- [1..(quot p 2)], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a + b + c == p]
But this approach becomes very slow as p
gets large. 但是随着p
变大,这种方法变得非常慢。
Is there a generally faster yet idiomatic Haskell way to accomplish the same thing for large p
? 对于大p
是否有一种通常更快但惯用的Haskell方法来完成相同的事情?
The comments make the good point that what you really need is a better algorithm. 这些评论指出,您真正需要的是更好的算法。
But let's try something different, and see what optimizations we can make to the current code: 但是,让我们尝试一些不同的东西,看看我们可以对当前代码进行哪些优化:
let rightTrianglesCubic p = [ (a,b,c) | c <- [1..quot p 2], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a + b + c == p]
First, note how we're looping over all the values of [1..b]
until we find one where a + b + c == p
. 首先,请注意我们如何遍历[1..b]
所有值,直到找到a + b + c == p
。 But the only value where that holds is a = p - b - c
, so we can skip the loop altogether, and make this into a quadratic algorithm: 但是唯一保留的值是a = p - b - c
,因此我们可以完全跳过循环,并将其变为二次算法:
let rightTrianglesQuadraticA p = [ (a,b,c) | c <- [1..quot p 2], b <- [1..c], let a = p - b - c, a^2 + b^2 == c^2]
There's a slight problem with this approach: 这种方法有一个小问题:
λ rightTrianglesCubic 120
[(30,40,50),(24,45,51),(20,48,52)]
λ rightTrianglesQuadraticA 120
[(40,30,50),(30,40,50),(45,24,51),(24,45,51),(48,20,52),(20,48,52),(0,60,60)]
We get some extra results! 我们得到一些额外的结果! This is because we ignored the implicit conditions made by a <- [1..b]
, namely 1 <= a
and a <= b
. 这是因为我们忽略a <- [1..b]
所隐含的条件,即1 <= a
和a <= b
。 So let's add those back in. 因此,让我们重新添加它们。
let rightTrianglesQuadraticB p = [ (a,b,c) | c <- [1..quot p 2], b <- [1..c], let a = p - b - c, a^2 + b^2 == c^2, 1 <= a, a <= b]
And now we get the right answer: 现在我们得到正确的答案:
λ rightTrianglesQuadraticB 120
[(30,40,50),(24,45,51),(20,48,52)]
Since each value of b
has a unique a
, the conditions 1 <= a
and a <= b
can be phrased as conditions on b
. 由于b
每个值都有唯一的a
,因此条件1 <= a
和a <= b
可以表述为b
条件。 1 <= a
is the same as 1 <= p - b - c
or b <= p - c - 1
. 1 <= a
与1 <= p - b - c
或b <= p - c - 1
。 a <= b
is the same as p - b - c <= b
or p - c <= 2*b
or div (p - c + 1) 2 <= b
. a <= b
与p - b - c <= b
或p - c <= 2*b
或div (p - c + 1) 2 <= b
。
This lets us shrink the bounds on the loop b <- [1..c]
: 这使我们缩小循环b <- [1..c]
的边界:
let rightTrianglesQuadraticC p = [ (a,b,c) | c <- [1..quot p 2], b <- [max 1 (div (p - c + 1) 2) .. min c (p - c - 1) ], let a = p - b - c, a^2 + b^2 == c^2]
We can even shrink the bounds on c <- [1..quot p 2]
by noting that in order for a < b < c
with a+b+c == p
, we must have c > p/3
: 注意,为了使a < b < c
和a+b+c == p
,我们必须缩小c <- [1..quot p 2]
的范围,我们必须使c > p/3
:
let rightTrianglesQuadraticD p = [ (a,b,c) | c <- [1 + quot p 3..quot p 2], b <- [max 1 (div (p - c + 1) 2) .. min c (p - c - 1) ], let a = p - b - c, a^2 + b^2 == c^2]
This is about as far as optimizing this particular code can go. 这与优化此特定代码有关。 For further performance improvement, we'll need to switch algorithms altogether. 为了进一步提高性能,我们需要完全切换算法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.