現在、Project Eulerの58番目の問題に取り組んでいます。
制限を 8% に設定すると、私のソリューションは問題なく実行されますが、その後 1 分を超えてしまいます。
私は素数の生成にライブラリを使用しており、自分で行うことは私には線形に見え、問題ないように見えるので、これは私を驚かせます。
明らかに、私は以前に多くの巨大なサンク、2 次 (またはさらに悪い) 関数、ボトルネックなどを見逃していましたが、ここでも同じである必要があります。
私が望むのは、コードが間違っているかどうか、または問題を処理する方法がばかげているかどうかを知るための私のソリューションのチェックです。後者の場合、私は甘やかされたくない.
基本的に私のコードはその目的のために機能しないため、私の質問はコードレビューではないと考えていますが、間違っている可能性があります。その場合、質問を適切な SE サイトに転送してください。
私はこれについて文芸的なプログラミングを試みたので、ファイルをダンプして詳細な説明を提供します。
私の.lhs(まあ、SO用にフォーマットされています)
素数を自分で処理しないので、コンパイラの助けが必要です。
{-# OPTIONS_GHC -Wall #-}
import Data.Numbers.Primes
まず、対角線上にある数のストリームを diags で構築します。これを行うには、これらの数値が特定の数値だけ連続して 4 回インクリメントされ、さらにこの数値 + 2 などで 4 回繰り返されることに注意して
ください。
次に、scanl (+) を使用してすべてを集計するだけで、リストが作成されます。
primesDiags :: [Int]
primesDiags = go diags primes
where
diags = scanl (+) 1 . concatMap (replicate 4) $ [2, 4..] :: [Integer]
このリストを取得したら、数値が合成数の場合はすべての数値を 0 に、素数の場合は 1 にマップします。これを効率的に行うために、ライブラリを使用して素数のストリームを提供し、2 つのリストを 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 つの項目をスキップし、この項目が 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
次に、正しい正方形を見つけたら、その指数を 2 倍して 1 を引くことによって、その辺の長さが得られます。
main :: IO ()
main = print $ nthSquare * 2 - 1
コードで遊びたい場合は、ここに貼り付けます。