2015-10-17 36 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