Haskell函数需要很长时间才能处理

时间:2009-09-27 10:53:31

标签: haskell optimization

我正在做项目欧拉的问题12,我必须找到第一个带有501除数的三角形数字。所以我用Haskell掀起了这个:

divS n = [ x | x <- [1..(n)], n `rem` x == 0 ]
tri n = (n* (n+1)) `div` 2
divL n = length (divS (tri n))
answer = [ x | x <- [100..] ,  501 == (divL x)]

第一个函数找到数字的除数。

第二个函数计算第n个三角形数

第3个函数查找列表的长度,它是三角形数的除数

第4个函数应返回具有501除数的三角数的值。

但到目前为止这种情况已经持续了一段时间而没有返回结果。答案是非常大还是我需要一些认真的优化来使这项工作在实际的时间内完成?

4 个答案:

答案 0 :(得分:4)

您需要使用除数函数的属性:http://en.wikipedia.org/wiki/Divisor_function

请注意,n和n + 1总是互质的,因此您可以通过乘以先前计算的值来得到d(n *(n + 1)/ 2)。

答案 1 :(得分:2)

使用所有数字&lt; = sqrt(n)的试验除法可能更快地对数字进行推理,然后使用因子分解来找到除数。

Sieve of Eratosthenes是寻找素数的经典方法,可以稍微修改以找出每个自然数的除数。您可以列出除每个数字之外的所有素数列表,而不是仅将每个非素数标记为“非素数”。 然后,您可以使用这些素数来计算完整的除数集,或者只计算它们的数量,因为这就是您所需要的。

另一种变化是不仅标记素数的倍数,而且标记所有自然数的倍数。然后你可以简单地使用一个计数器来跟踪每个数字的除数。

您也可以查看The Genuine Sieve of Eratosthenes,这解释了原因 试验部门比真正的筛子慢。

最后,您应该仔细查看Haskell中不同类型的数组。我认为使用ST monad来实现筛子可能更容易,但是如果可以确保更新函数是严格的,则可以使用accumArray来实现正确的复杂性。我从来没有设法让这个工作,所以你在这里自己。

答案 2 :(得分:0)

如果您使用的是C而不是Haskell,那么您的功能仍然需要很长时间。

为了加快速度,您需要使用上述答案中的建议来改进算法。我建议相应地更改标题和问题描述。之后,我将删除此评论。

如果您愿意,我可以通过分享我的解决方案来解决问题。

现在我会给你我的顶级代码:

main =
  print .
  head . filter ((> 500) . length . divisors) .
  map (figureNum 3) $ [1..]

算法改进在于divisors功能。您可以使用rawicki的建议进一步改进它,但这已经不到100毫秒了。

答案 3 :(得分:-1)

一些优化提示:

  • 检查1和sqrt(n)之间的除数。我保证你不会发现任何超过这个限制(除了数字本身)。
  • 不要建立一个除数列表并计算列表,但要直接计算它们。