项目Euler 58 - 我的解决方案中是否存在反模式?

时间:2012-10-20 13:26:49

标签: haskell

我目前正在处理来自58th problemProject Euler

虽然当我将限制设置为8%时,我的解决方案运行正常,但在此之后超过一分钟。

这让我感到惊讶,因为我使用了一个库来进行素数生成,而我自己所做的事情对我来说看起来很线性,很好。

虽然很明显我已经错过了很多巨大的雷鸣,二次(或更糟)的功能,瓶颈等等,但这里必须再次相同。

我想要的是了解我的解决方案,知道代码是错误的,还是我处理愚蠢问题的方式。在后一种情况下,我不想被宠坏。

我认为我的问题不是代码审查,因为基本上我的代码不能用于其目的,但我可能错了,在这种情况下请将问题转移到相应的SE网站。

我为这个编写了文字编程,所以我只是转储我的文件以提供进一步的解释。

我的.lhs(嗯,格式化为SO)

我们自己不处理素数,我们需要编译器的帮助。

{-# OPTIONS_GHC -Wall #-}

import Data.Numbers.Primes

首先,我们在diags中构造数字流 在对角线上。为此,我们注意到这些数字会增加 连续4次按一定数量,然后再用这个数字+ 2等。
复制4 [2,4 ..]会给我们一个增量列表。

然后我们只需要用scanl(+)聚合所有这些,我们就有了 我们的清单。

primesDiags :: [Int]
primesDiags = go diags primes
  where
    diags = scanl (+) 1 . concatMap (replicate 4) $ [2, 4..] :: [Integer]

一旦我们有了这个列表,如果数字是复合的,我们将所有数字映射到0 如果数字是素数则为1。为了有效地做到这一点,我们使用库来 提供素数流并通过运行它们映射两个列表 只有一次。

    go dss@(d:ds) pss@(p:ps) | d < p = 0:go ds pss
                             | d > p = go dss ps
                             | otherwise = 1:go ds ps

然后我们告诉ghc我们知道为什么我们的模式匹配不完整

    go _ _ = undefined -- we operate on streams

现在我们拥有解决问题所需的一切。下一步是找到 在哪个方格我们越过我们寻求发现的特定限制。要做到这一点,我们 只需更新累加器即可表示我们遇到的素数 直到这一点,我们跟踪我们所在的广场的指数。

我们在2处开始递归,以便跟踪分解行为。 这就是为什么我们跳过primesDiags中的一个项目,因为这个项目是1我们 将我们的acc设置为0(1不是素数)。

nthSquare :: Int
nthSquare = go 2 (tail primesDiags) 0
  where
    go n (w:x:y:_:ds) primeN | 8 * primeN' < compositeN' = n
                             | otherwise = go (n + 1) ds primeN'
      where
        total = 4 * n - 3
        delta = sum [w, x, y]
        primeN' = primeN + delta
        compositeN' = total - primeN'
    go _ _ _ = undefined -- we operate on streams

然后,一旦我们发现正确的方形,它的边长 通过将其指数加倍并减去一个来获得。

main :: IO ()
main = print $ nthSquare * 2 - 1
如果你想使用代码,

Here是一个粘贴。

1 个答案:

答案 0 :(得分:10)

不是其余的根本无法改进,但是用于素数生成的库不是太快。使用你的代码,我得到了

$./euler58 +RTS -s -RTS
11297
  30,958,460,200 bytes allocated in the heap
   4,671,021,104 bytes copied during GC
         495,832 bytes maximum residency (2822 sample(s))
          47,664 bytes maximum slop
               3 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0     56551 colls,     0 par    5.96s    5.97s     0.0001s    0.0004s
  Gen  1      2822 colls,     0 par    1.60s    1.61s     0.0006s    0.0014s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time   21.47s  ( 21.50s elapsed)
  GC      time    7.56s  (  7.57s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time   29.04s  ( 29.08s elapsed)

  %GC     time      26.1%  (26.0% elapsed)

  Alloc rate    1,441,874,868 bytes per MUT second

  Productivity  73.9% of total user, 73.8% of total elapsed

将导入更改为

import Math.NumberTheory.Primes

(来自arithmoi包 - 免责声明,我是作者)和primesDiags的第一行

primesDiags = go diags (map fromInteger primes)

结果是

$ ./aeuler58 +RTS -s -RTS
11297
   1,986,441,440 bytes allocated in the heap
      25,254,256 bytes copied during GC
         220,328 bytes maximum residency (34 sample(s))
         158,984 bytes maximum slop
               2 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0      3761 colls,     0 par    0.06s    0.06s     0.0000s    0.0002s
  Gen  1        34 colls,     0 par    0.00s    0.00s     0.0001s    0.0001s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.96s  (  0.96s elapsed)
  GC      time    0.06s  (  0.06s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    1.02s  (  1.02s elapsed)

  %GC     time       5.9%  (5.9% elapsed)

  Alloc rate    2,064,058,991 bytes per MUT second

  Productivity  94.1% of total user, 94.1% of total elapsed

表明你的部分代码是体面的。您可以通过使用其中一个尖峰上有正方形的事实来改进它,因此根本不需要考虑它。

另一点是,您可以只检查对角线上的值(忽略正方形),而不是计算所有质数,如果您有快速检查,则检查它们中的哪一个是素数。是否更快取决于主要检查。