合格的Haskell列表推导的有效替代方案

时间:2015-10-17 14:05:39

标签: performance haskell list-comprehension

作为Haskell中合格列表推导的一个例子,Learn You 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] 

但随着p变大,这种方法变得非常

对于大型p,是否存在通常更快但惯用的Haskell方法来完成同样的事情?

1 个答案:

答案 0 :(得分: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 可以在b上表达为条件。 1 <= a1 <= p - b - c或{}相同 b <= p - c - 1a <= bp - b - c <= bp - 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]

我们甚至可以缩小c <- [1..quot p 2]上的界限,注意a < b < c a+b+c == p 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]

  ParseQuery<ParseObject> query = ParseQuery.getQuery("GameScore");
query.whereEqualTo("playerName", "Dan Stemkoski");
query.findInBackground(new FindCallback<ParseObject>() {
    public void done(List<ParseObject> scoreList, ParseException e) {
        if (e == null) {
            Log.d("score", "Retrieved " + scoreList.size() + " scores");
        } else {
            Log.d("score", "Error: " + e.getMessage());
        }
    }
});

这就是优化这个特定代码所能实现的。为了进一步提高性能,我们需要完全切换算法。