简体   繁体   English

合格的Haskell列表理解的有效替代方法

[英]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 <= aa <= 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 <= aa <= b可以表述为b条件。 1 <= a is the same as 1 <= p - b - c or b <= p - c - 1 . 1 <= a1 <= p - b - cb <= 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 <= bp - b - c <= bp - c <= 2*bdiv (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 < ca+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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM