Haskell - 列表推导不能枚举N×N

时间:2013-05-14 20:53:56

标签: performance list haskell list-comprehension

我必须编写一个函数,它返回所有对(x,y)的列表,其中x, y∈N,

  • x是两个自然数的乘积(x = a•b,其中a,b∈N)
  • x确实大于5但真的小于500,
  • y是一个平方数(y =c²,其中c∈N)不大于1000,
  • x是y的除数。

我的尝试:

listPairs :: [(Int, Int)] 
listPairs = [(a*b, y) | y <- [0..], a <- [0..], b <- [0..], 
                        (a*b) > 5, (a*b) < 500, (y*y) < 1001, 
                        mod y (a*b) == 0]

但它没有返回任何东西,而且计算机在它上面工作很多。

但是,如果我为aby e选择较小的范围。 G。 [0..400],它需要一分钟,但它会返回正确的结果。

那我怎么能解决性能问题呢?

1 个答案:

答案 0 :(得分:1)

因此,无限列表上的嵌套列表推导当然不会终止。

幸运的是,你的名单不是无限的。有一个限制。如果x = a*b < 500,则我们知道它必须是a < 500b < 500。此外,c = y*y < 1001只是y < 32。所以,

listPairs :: [(Int, Int)] 
listPairs = 
    [(x, c*c) | c <- [1..31], a <- [1..499],    -- a*b < 500 ==> b<500/a ,
                b <- [a..min 499 (div 500 a)],  -- a*b==b*a  ==> b >= a
                let x = a*b, x > 5,  
                -- (a*b) < 500, (c*c) < 1001,   -- no need to test this
                rem (c*c) x == 0]    

mod 0 n == 0平凡地持有,所以我在这里从“自然数”中排除0。

此处仍有一些重复项,即使我们已将b值限制为b >= a中的x=a*b,因为x可以有多个表示形式(例如{ {1}})。

您可以使用1*6 == 2*3删除它们。