totients のリスト用にふるいベースのジェネレーターを作成し、合計を 1000000 にしたいと考えています。
applyEvery :: (a -> a) -> Int -> [a] -> [a]
applyEvery f n xs = xf ++ (\(y:ys) -> f y : applyEvery f n ys) xb
where
(xf, xb) = splitAt (n - 1) xs
totients :: [Int]
totients = 1 : sieve [2..] [2..]
where
sieve (x:xs) (y:ys)
| x == y = (y - 1) : sieve xs (propagatePrime x ys)
| otherwise = y : sieve xs ys
propagatePrime j = applyEvery (\x -> (quot x j)*(j - 1)) j
totientSum :: Int -> Int
totientSum n = sum $ take n totients
totientSum n
40000を超える計算をn
行うと、ghci は評価に時間がかかり、膨大な量のメモリを消費し始めます。実行可能ファイルへのコンパイルは役に立ちません。これは、Haskell が遅延評価を処理する方法と関係があると思います。
上記の関数のメモリ消費を改善するために厳密性を選択的に適用して、最大 1000000 までの合計を計算できるかどうかを知りたいです。また、リストを使用してこれを行うより良い方法があるかどうかも知りたいです。ランダム アクセス データ構造を使用する必要がある場合。全合計を計算するためのより高速なアルゴリズムを知っている場合は、参考文献を共有してください。
の定義がapplyEvery
違うのではないかと思い、以下の他の実装も試してみましたが、どちらも上記の定義よりも多くのメモリを消費しているようでした。
-- <https://www.reddit.com/2tpqip/>
applyEvery' :: (a -> a) -> Int -> [a] -> [a]
applyEvery' f n = zipWith ($) (cycle (replicate (n - 1) id ++ [f]))
applyEvery'' :: (a -> a) -> Int -> [a] -> [a]
applyEvery'' f n xs = xf ++ (\(y:ys) -> f y : applyEvery'' f n ys) xb
where
xf = take (n - 1) xs
xb = drop (n - 1) xs