我目前正在处理Project Euler的第58个问题。
当我将限制设置为 8% 时,我的解决方案运行良好,但在那之后它超过了一分钟。
这让我感到惊讶,因为我使用一个库来生成素数,而且我自己做的事情对我来说看起来是线性的,而且很好。
虽然很明显我之前错过了很多巨大的 thunk、二次(或更糟)函数、瓶颈等,但这里肯定又是一样的。
我想要的是检查我的解决方案,以了解代码是否错误,或者我处理问题的方式是否愚蠢。在后一种情况下,我不想被宠坏。
我认为我的问题不是代码审查问题,因为基本上我的代码不能达到其目的,但我可能错了,在这种情况下,请将问题转移到适当的 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
如果您想使用代码,这是一个粘贴。