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

Я думаю, что общая идея заключается в использовании другого алгоритма, т. Е. Не грубой силы. Это не понимание списков, поскольку это медленное, это классический пример алгоритма грубой силы вложенных циклов.Сложность этого алгоритма где-то вокруг «O (n^3)», а не хорошая сложность, так как «n» растет. – bheklilr

+0

Правильно, это алгоритм/математическая проблема, а не проблема Haskell/производительности. Насколько вы заинтересованы в решении этой конкретной проблемы поиска правильных треугольников с заданным периметром? Эффективный способ - использовать параметризацию троек Pythagorean и начать с факторинга 'p' ... –

+0

@ReidBarton: Да, хорошие моменты, которые частично отвечают на вопрос: ограничения не применяются к спецификациям домена. Например, все значения '(a, b, c)' для 'c <- [1 .. (p 2)]', 'b <- [1..c]' и 'a <- [1 ..b] ', и * then *, тогда применяются ограничения (в отличие, например, ограничение' 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 <= 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] 

Мы можем даже термоусадочной границы на 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] 

Это касается того, насколько оптимизирован этот конкретный код. Для дальнейшего улучшения производительности нам нужно будет полностью переключить алгоритмы.

+0

Спасибо. Это именно то, что я искал: я хотел придерживаться понимания списка, но не хочу быть * O (n³) *. Я также хотел подтвердить, что мне интересно: что ограничения после '|' не «решены» каким-либо образом (как вы это делали «вручную» выше). – orome