ハスケルのエラトステネスのふるい
-
27-09-2019 - |
質問
私はハスケルでいくつかの古典的な問題を解決して機能的なスキルを開発していますが、これで提案されている最適化を実装する問題があります 「プログラミングプラクシス」 サイト:
この問題には3つの解決策があり、3番目の問題は2番目のソリューションに比べて遅すぎます。誰かが私のコードの改善を提案できますか?
私の実装は次のとおりです。
-- primeira implementação
primes n
| n < 2 = []
| n == 2 = [2]
| n `mod` 2 == 0 = primes'
| otherwise = if (find (\x -> n `mod` x == 0) primes') == Nothing then
n:primes'
else
primes'
where primes' = primes (n - 1)
-- segunda implementação
primes' :: Integer -> [Integer]
primes' n = sieve $ 2 : [3,5..n]
where sieve :: [Integer] -> [Integer]
sieve [] = []
sieve l@(x:xs)
| x*x >= n = l
| otherwise = x : sieve list'
where list' = filter (\y -> y `mod` x /= 0) xs
-- terceira implementação
primes'' :: Integer -> [Integer]
primes'' n = 2 : sieve 3 [3,5..n]
where sieve :: Integer -> [Integer] -> [Integer]
sieve _ [] = []
sieve m l@(x:xs)
| m*m >= n = l
| x < m*m = x : sieve m xs
| otherwise = sieve (m + 2) list'
where list'= filter (\y -> y `mod` m /= 0) l
解決
3回目の改訂版の問題は、次の要素を選択する方法を選択する方法です。あなたは2で無差別に増加します。問題は、不必要な数字をふるいにかけることです。たとえば、このバージョンでは、最終的に9をMとして渡すことになります。リストに登録されていなくても、9をフィルタリングするために余分な再帰を行います。最初の場所(3の最初のフィルターで削除されていたので)
2番目のバージョンでは、ふるいにかけられる数の正方形を通り過ぎてフィルタリングを開始しませんが、不必要なふるいする値を選択することはありません。
言い換えれば、あなたは3とnの間のすべての奇数をふるいにかけることになると思います。代わりに、以前のパスでまだ削除されていないすべての奇数をふるいにかける必要があります。
現在のSIFT値の正方形でふるいを開始する最適化を正しく実装するには、バックが要素> = SIFT値の正方形を含む背面にふるい分けながら、リストの前面を保持する必要があると思います。これにより、連結を使用することが強制されると思います。++を使用して誘導されるオーバーヘッドをキャンセルするのに十分な最適化が十分であるかどうかはわかりません。
他のヒント
初めに、 mod
遅いので使用してください rem
それが重要ではない状況で(基本的にネガを扱っていないとき)。第二に、使用します 基準 何がより速いのか、そして実際に最適化とは何かを(自分自身に)示すこと。私はこれであなたに完全な答えを与えていないことを知っていますが、それはあなた(および他の潜在的な答え者)が開始するのに良い場所ですので、ここにいくつかのコードがあります:
import List
import Criterion.Main
main = do
str <- getLine
let run f = length . f
input = read str :: Integer
defaultMain [ bench "primes" (nf (run primes) input)
, bench "primes'" (nf (run primes') input)
, bench "primes''" (nf (run primes'') input)
, bench "primesTMD" (nf (run primesTMD) input)
, bench "primes'TMD" (nf (run primes'TMD) input)
, bench "primes''TMD" (nf (run primes''TMD) input)
]
putStrLn . show . length . primes'' $ (read str :: Integer)
-- primeira implementação
primes n
| n < 2 = []
| n == 2 = [2]
| n `mod` 2 == 0 = primes'
| otherwise = if (find (\x -> n `mod` x == 0) primes') == Nothing then
n:primes'
else
primes'
where primes' = primes (n - 1)
primesTMD n
| n < 2 = []
| n == 2 = [2]
| n `mod` 2 == 0 = primes'
| otherwise = if (find (\x -> n `rem` x == 0) primes') == Nothing then
n:primes'
else
primes'
where primes' = primesTMD (n - 1)
-- segunda implementação
primes' :: Integer -> [Integer]
primes' n = sieve $ 2 : [3,5..n]
where sieve :: [Integer] -> [Integer]
sieve [] = []
sieve l@(x:xs)
| x*x >= n = l
| otherwise = x : sieve list'
where list' = filter (\y -> y `mod` x /= 0) xs
primes'TMD :: Integer -> [Integer]
primes'TMD n = sieve $ 2 : [3,5..n]
where sieve :: [Integer] -> [Integer]
sieve [] = []
sieve l@(x:xs)
| x*x >= n = l
| otherwise = x : sieve list'
where list' = filter (\y -> y `rem` x /= 0) xs
-- terceira implementação
primes'' :: Integer -> [Integer]
primes'' n = 2 : sieve 3 [3,5..n]
where sieve :: Integer -> [Integer] -> [Integer]
sieve _ [] = []
sieve m l@(x:xs)
| m*m >= n = l
| x < m*m = x : sieve m xs
| otherwise = sieve (m + 2) list'
where list'= filter (\y -> y `mod` m /= 0) l
primes''TMD :: Integer -> [Integer]
primes''TMD n = 2 : sieve 3 [3,5..n]
where sieve :: Integer -> [Integer] -> [Integer]
sieve _ [] = []
sieve m l@(x:xs)
| m*m >= n = l
| x < m*m = x : sieve m xs
| otherwise = sieve (m + 2) list'
where list'= filter (\y -> y `rem` m /= 0) l
使用してバリアントのランタイムの改善に注意してください rem
:
$ ghc --make -O2 sieve.hs
$./sieve
5000
...
benchmarking primes
mean: 23.88546 ms, lb 23.84035 ms, ub 23.95000 ms
benchmarking primes'
mean: 775.9981 us, lb 775.4639 us, ub 776.7081 us
benchmarking primes''
mean: 837.7901 us, lb 836.7824 us, ub 839.0260 us
benchmarking primesTMD
mean: 16.15421 ms, lb 16.11955 ms, ub 16.19202 ms
benchmarking primes'TMD
mean: 568.9857 us, lb 568.5819 us, ub 569.4641 us
benchmarking primes''TMD
mean: 642.5665 us, lb 642.0495 us, ub 643.4105 us
私はあなたがあなた自身の教育のためにこれをしているのを見ていますが、それは関連するリンクに注目する価値があります haskell.orgの素数 そして速い プライムパッケージ ハッケージで。