2015-10-17 65 views
2

作为Haskell中合格列表解析的一个例子,Learn You a Haskell教程提供了一个列表理解的例子,该列表理解建议寻找具有给定边界p的正确三角形的一般方法,表示为元组:合格Haskell列表解析的高效替代方案

λ> 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] 

但是,这种方法变得非常作为p变大。

是否有一个普遍更快,但惯用的Haskell方式来完成大p相同的事情?

+7

我认为一般的想法是使用不同的算法,即不是蛮力。这并不是列表理解错误,这是一个嵌套for循环的蛮力算法的典型例子。这个算法的复杂性在'O(n^3)'周围,随着'n'变大,复杂度不是很高。 – bheklilr

+0

对,这是一个算法/数学问题,而不是Haskell /性能问题。你对解决这个寻找具有规定边界的直角三角形问题有多大兴趣?有效的方法是使用毕达哥拉斯三元组的参数化,并通过分解“p”开始...... –

+0

@ReidBarton:是的,好点,部分回答了这个问题:约束不适用于域规范。例如,对于'c < - [1 ...(p2)]','b < - [1..c]'和'a < - [1]的所有值'(a,b,c) ..b]'将被检查,然后*然后*然后约束被应用(相反,也就是说,将'a'限制为等于'pcb'的值。这是我询问的Haskell-y问题。 – orome

回答

6

这些评论意味着你真正需要的是更好的算法。

但是,让我们尝试不同的东西,看看我们可以对当前代码什么的优化:

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] 

首先,请注意我们如何遍历的[1..b]所有的值,直到我们找到一个地方a + b + c == p。但是,在保存的唯一价值是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] 

有一个小问题,这种方法:

λ 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)] 

我们得到一些额外的结果!这是因为我们忽略了a <- [1..b]所做的隐含条件,即1 <= aa <= b。因此,让我们添加这些回

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] 

而现在我们得到正确的答案:

λ rightTrianglesQuadraticB 120 
[(30,40,50),(24,45,51),(20,48,52)] 

由于b每个值都有一个唯一的a,条件1 <= aa <= b 可以表述为条件在b1 <= a1 <= p - b - cb <= p - c - 1相同。 a <= bp - b - c <= bp - c <= 2*bdiv (p - c + 1) 2 <= b相同。

这让我们缩小环路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] 

我们甚至可以通过提的是,为了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] 

这是关于优化这个特定的代码可以去。为了进一步提高性能,我们需要完全切换算法。

+0

谢谢。这正是我想要的:我想坚持一个列表理解,但不想成为* O(n³)*。我也想确认我在想什么:''后面的约束'没有以任何方式“解决”(正如你上面所做的“手工”)。 – orome