我目前正在处理来自58th problem的Project Euler。
虽然当我将限制设置为8%时,我的解决方案运行正常,但在此之后超过一分钟。
这让我感到惊讶,因为我使用了一个库来进行素数生成,而我自己所做的事情对我来说看起来很线性,很好。
虽然很明显我已经错过了很多巨大的雷鸣,二次(或更糟)的功能,瓶颈等等,但这里必须再次相同。
我想要的是了解我的解决方案,知道代码是错误的,还是我处理愚蠢问题的方式。在后一种情况下,我不想被宠坏。
我认为我的问题不是代码审查,因为基本上我的代码不能用于其目的,但我可能错了,在这种情况下请将问题转移到相应的SE网站。
我为这个编写了文字编程,所以我只是转储我的文件以提供进一步的解释。
我们自己不处理素数,我们需要编译器的帮助。
{-# 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是一个粘贴。
答案 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
表明你的部分代码是体面的。您可以通过使用其中一个尖峰上有正方形的事实来改进它,因此根本不需要考虑它。
另一点是,您可以只检查对角线上的值(忽略正方形),而不是计算所有质数,如果您有快速检查,则检查它们中的哪一个是素数。是否更快取决于主要检查。