这些评论意味着你真正需要的是更好的算法。
但是,让我们尝试不同的东西,看看我们可以对当前代码什么的优化:
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 <= a
和a <= 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 <= a
和a <= b
可以表述为条件在b
。 1 <= a
与1 <= p - b - c
或 b <= p - c - 1
相同。 a <= b
与p - b - c <= b
或p - c <= 2*b
或 div (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 < 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]
这是关于优化这个特定的代码可以去。为了进一步提高性能,我们需要完全切换算法。
我认为一般的想法是使用不同的算法,即不是蛮力。这并不是列表理解错误,这是一个嵌套for循环的蛮力算法的典型例子。这个算法的复杂性在'O(n^3)'周围,随着'n'变大,复杂度不是很高。 – bheklilr
对,这是一个算法/数学问题,而不是Haskell /性能问题。你对解决这个寻找具有规定边界的直角三角形问题有多大兴趣?有效的方法是使用毕达哥拉斯三元组的参数化,并通过分解“p”开始...... –
@ReidBarton:是的,好点,部分回答了这个问题:约束不适用于域规范。例如,对于'c < - [1 ...(p2)]','b < - [1..c]'和'a < - [1]的所有值'(a,b,c) ..b]'将被检查,然后*然后*然后约束被应用(相反,也就是说,将'a'限制为等于'pcb'的值。这是我询问的Haskell-y问题。 – orome