我已经为 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
超过n
40000 时,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