{-# OPTIONS_GHC -O2 -fno-cse #-}
{-# LANGUAGE BangPatterns #-}
---------------------------------------------------------------------------------------------
--- Sieve of Eratosthenes Comparison Table --- Treefold Merge with Wheel --- by Will Ness --- *)
---------------------------------------------------------------------------------------------
import Array
main0 =
let pairs (x:y:t) = [x,y]:pairs t
in
case spec of
[n
] -> print $ slice
10 primesTMWE n
-- 10 up to n-th [n
,lim
] -> print $ slice
10 (primesLimQE lim
) n
-- 10 up to n-th, under limit
slice n xs ending
= take n
$ drop (ending
-n
) xs
----- (on using INT: WolframAlpha( PrimePi[2147483647] ) => 105,097,565 :
----- no chance to get there in 15 sec limit on ideone.com anyway)
primesLimGQAE m = sieve 3 $
accumArray (\b c->False) True (3,m)
[(i,()) | i<-[4,6..m]]
where -- (* ... on array, from squares *)
sieve !p !a
| p*p > m = 2 : [i | (i,True) <- assocs a]
| a!p = sieve (p+2) (accum (\b c->False) a [(i,()) | i <- [p*p,p*p+2*p..m]])
{- well, this is disappointing. I hoped for an array update ((//) a b)
to take up time ~ O(|b|), to give it TRUE complexity of a Sieve of E.,
but it is apparently ~ O(|a|) and so gives it even WORSE
complexity than that of simple listMinus versions (where length
of first-arg list diminishes w/ iterations, and here it always
stays the same, for the same array) ... so to update ONE PLACE in array
has a cost of LENGTH_OF(ARRAY) ?????????? what is this????
... well of course the documentation says "makes an altered copy of ..."
but since it is passed along to further iterations, one would expect
the implementation to recognize the fact that the previous one
EXISTS NO MORE and just UPDATE the dang ***UNIQUE*** thing!
and this is not at all unreasonable: making a copy of an array
should be an O(1) operation after all!!!!!!!!!!! not O(|a|) in any case.
... will bangPatterns help ? ... no, it just runs slower...
here's the working Array version, which needs to be segmented
into the lmited-sized chunks of array betwen the primes squares
because as it is, its memory usage grows rapidly:
primes () = 2: primes' -- marking off composites on Array segments
where -- between consecutive primes squares, on odds
primes' = 3: sieve primes' 3 []
sieve (p:ps) x fs =
let
q = (p*p-x)`div`2
a = accumArray (\b c-> False) True
(1,q-1)
[(i,()) | (s,y) <- fs, i <- [y+s,y+s+s..q]]
in
[i*2 + x | (i,e) <- assocs a, e]
++ sieve ps (p*p)
((p,0):[(s,rem (y-q) s) | (s,y) <- fs])
-}
primesT = sieve [2..] where
sieve
(p:xs
) = p : sieve
[x
| x
<- xs
, rem x p
/= 0] -- (* Turner's Trial Division *)
primesE = sieve [2..] where
sieve (p:xs) = p : sieve (minus xs [p,p+p..]) -- (* Sieve of Eratosthenes *)
primesQE = 2 : sieve [3,5..] where -- (* ... from squares *)
sieve (p:xs)
| p > 46340 = p : xs -- prevent INT wraparound
| True = p : sieve (minus xs [p*p,p*p+2*p..])
primesEU = 2 : sieve [3,5..] where -- (* Euler's Sieve *)
sieve (p:xs)
| p > 46340 = p : xs -- Prime[4792,..] = 46337, 46349 ...
| True = p : sieve (minus xs [p*x | x <- p:xs])
-------------------------------------------------------------------------------------------------------
----- it is NOT about *WHERE* to start removing the multiples, BUT **WHEN** to start doing it! ... ----
-------------------------------------------------------------------------------------------------------
primesPT
= 2 : sieve
[3,5..] (tail primesPT
) 9 where
sieve (x:xs) ps@ ~(p:ps') q -- (* Postponed Turner's *)
| x < q = x : sieve xs ps q
| True
= sieve
[x
| x
<- xs
, rem x p
/= 0] ps
' (head ps'^2) -------------------------------------------------------------------------------------------------------
primesST = 2 : primes'
where -- (* Segmented Turner's sieve *)
primes' = 3 : sieve primes' 3 0
sieve
(p:ps
) x k
= let pfx
= take k primes
' in [n | n <- [x+2,x+4..p*p-2], and [rem n f /= 0 | f <- pfx]]
++ sieve ps (p*p) (k+1)
-------------------------------------------------------------------------------------------------------
primesPE :: [Int]
primesPE = 2 : sieve [3,5..] (tail primesPE) 9
where
sieve (x:xs) ps@ ~(p:ps') q -- (* Postponed Eratosthenes *)
| x < q = x : sieve xs ps q
| True = sieve (xs `minus` [q,q+2*p..]) ps' (head ps'^2)
-------------------------------------------------------------------------------------------------------
primesLME = 2 : ([3,5..] `minus` fold [[p*p,p*p+2*p..] | p <- primes']) -- separate feed 2save space
where
primes' = 3 : ([5,7..] `minus` fold [[p*p,p*p+2*p..] | p <- primes'])
fold ((x:xs):t) = x : (xs `union` fold t) -- (* Linear Merge Eratosthenes *)
-------------------------------------------------------------------------------------------------------
primesTME :: [Int]
primesTME = 2 : ([3,5..] `minus` foldt [[p*p,p*p+2*p..] | p <- primes']) -- iterate (+ (2*p)) (p*p)
where
primes' = 3 : ([5,7..] `minus` foldt [[p*p,p*p+2*p..] | p <- primes']) -- enumFromThen (p*p) (p*p+2*p)
foldt ((x:xs):t) = x : union xs (foldt (pairs t))
pairs ((x:xs):ys:t) = (x : union xs ys) : pairs t -- (* Treefold Merge Eratosthenes *)
-------------------------------------------------------------------------------------------------------
primesTMWE = 2:3:5:7: gaps 11 wheel (fold3t $ roll 11 wheel primes')
where
primes' = 11: gaps
13 (tail wheel
) (fold3t
$ roll
11 (2:
tail wheel
) primes
') -- for -fno-cse fold3t ((x:xs): ~(ys:zs:t)) = x : union xs (union ys zs)
`union` fold3t (pairs t) -- fold3t: 5% speedup vs foldt
pairs ((x:xs):ys:t) = (x : union xs ys) : pairs t
wheel = 2:4:2:4:6:2:6:4:2:4:6:6:2:6:4:2:6:4:6:8:4:2:4:2:
4:8:6:4:6:2:4:6:2:6:6:4:2:4:6:2:6:4:2:4:2:10:2:10:wheel
gaps k ws@(w:t) cs@ ~(c:u) | k==c = gaps (k+w) t u -- (* better fold, w/ Wheel! *)
| True = k : gaps (k+w) t cs
roll k ws@(w:t) ps@ ~(p:u) | k==p = scanl (\c d->c+p*d) (p*p) ws
: roll (k+w) t u
| True = roll (k+w) t ps
primesAME :: [Int] -- (* Array Merge Eratosthenes *)
primesAME = 2 : ([3,5..] `minus` ajoin [[p*p,p*p+2*p..] | p <- primes'])
where
primes' = 3 : ([5,7..] `minus` ajoin [[p*p,p*p+2*p..] | p <- primes'])
ajoin ((x:xs):t) = [x] -- x : go [xs] t
m = 10000
-- go sts ((x:xs):t) =
{-
-- reified tree
data T = N {c::Int, p::Int, ws::[Int]} | L {p::Int, ws::[Int]}
primesTMWRE :: [Int]
primesTMWRE = 2:3:5:7: gaps 11 wheel (fold3t mults)
where -- double feed, to fix space leak
primes' = 11: gaps 13 (tail wheel) (fold3t mults)
mults = roll 11 wheel primes'
fold3t (L p ws: ~(b:c:t)) = x : union xs (union ys zs)
`union` fold3t (pairs t)
pairs ((x:xs):ys:t) = (x : union xs ys) : pairs t
wheel = 2:4:2:4:6:2:6:4:2:4:6:6:2:6:4:2:6:4:6:8:4:2:4:2:
4:8:6:4:6:2:4:6:2:6:6:4:2:4:6:2:6:4:2:4:2:10:2:10:wheel
gaps k ws@(w:t) cs@ ~(c:u) | k==c = gaps (k+w) t u -- (* fold3, 2x feed, w/ Wheel! *)
| True = k : gaps (k+w) t cs
roll k ws@(w:t) ps@ ~(p:u) | k==p = L p ws -- scanl (\c d->c+p*d) (p*p) ws
: roll (k+w) t u
| True = roll (k+w) t ps
-}
-------------------------------------------------------------------------------------------------------
minus xs
@(x:xt
) ys
@(y:yt
) = case compare x y
of LT -> x : minus xt ys
EQ -> minus xt yt
GT -> minus xs yt
minus a b = a
union xs
@(x:xt
) ys
@(y:yt
) = case compare x y
of LT -> x : union xt ys
EQ -> x : union xt yt
GT -> y : union xs yt
union a [] = a
union [] b = b
-------------------------------------------------------------------------------------------------------
----- *it is about* removing ONLY what MUST be removed, not especially *WHEN* to start doing it -------
-------------------------------------------------------------------------------------------------------
-------- bounded versions, generating primes UP TO a LIMIT: -------------------------------------------
primesLimT m = sieve [2..m] where
sieve
(p:xs
) = p : sieve
[x
| x
<- xs
, rem x p
/= 0] -- (* Turner's Trial Division/lim *) sieve [] = []
primesLimGT m = sieve [2..m] where
sieve (p:xs)
| p*p > m = p : xs
| True
= p : sieve
[x
| x
<- xs
, rem x p
/= 0] -- (* ... guarded *) sieve [] = []
primesLimE m = sieve [2..m] where
sieve (p:xs) = p : sieve (minus xs [p,p+p..m]) -- (* Sieve of Eratosthenes/lim *)
sieve [] = []
primesLimGE m = sieve [2..m] where -- (* ... guarded *)
sieve (p:xs)
| p*p > m = p : xs -- to STOP EARLY is essential!
| True = p : sieve (minus xs [p,p+p..m])
sieve [] = []
primesLimGQE m = 2 : sieve [3,5..m] where -- (* ... from squares *)
sieve (p:xs)
| p*p > m = p : xs -- early cut-off on first p such that p*p>m
| True = p : sieve (minus xs [p*p,p*p+2*p..m]) -- to start from squares is just small improvement
sieve [] = []
primesLimQE
:: Int -> [Int] -- no Guarded:primesLimQE m = 2 : sieve [3,5..m] where -- (* ... from squares *)
sieve (p:xs)
| p > mroot = p : sieve (minus xs []) -- no early cut-off, just INT wraparound guarded off
| True = p : sieve (minus xs [p*p,p*p+2*p..m]) -- prevent out-of-bounds (p*p)
sieve [] = []
primesLimGEU m = 2 : sieve [3,5..m] where -- (* Euler's Sieve/lim, guarded *)
sieve (p:xs)
| p*p > m = p : xs -- ... prevent INT wraparound too
| True = p : sieve (minus xs [p*x | x <- p:xs])
sieve [] = []
{-========================================================================================
2,000 10,000 15,000 17,000 19,000
------------------------------------------------------------------------------------------
T 0.08s-3.7MB 2.65s-4.7MB 7.01s-5.8MB 9.52s-5.8MB 12.83s-5.8MB Turner's
n^2.17 n^2.40 n^2.45 n^2.68
LimT 2.72-4.7 7.06-5.8 /limit
------------------------------------------------------------------------------------------
E 0.05s-4.8MB 2.10s-5.8MB 5.95s-6.8MB 8.82s-6.8MB 14.00s-7.8MB Eratosthenes'
n^2.30 n^2.60 n^3.14 n^4.15
LimE 1.17-4.7 3.15-4.7 25k:11.55-5.8 /limit
n^2.44 n^2.54
QE 0.05s-3.7MB 1.19s-4.7MB 2.00s-4.7MB 50k:7.72-4.7 78,498:12.31-4.7 E. from sQuares
n^1.97 n^1.28 n^1.12 n^1.03
------------------------------------------------------------------------------------------
EU 0.41s-48.8MB 4k:1.71s-167.6MB EUler's
n^2.06
LimGEU 4k:0.04- 8.8 15k:0.27-24.2 50k:2.06-166.5 /limit/guard
n^1.44 n^1.69
==========================================================================================
==========================================================================================
78,499 200,000 400,000 1,000,000 2,000,000
------------------------------------------------------------------------------------------
LimGQAE 3.59-20.2 150k:10.34-36.5 E/arrays
n^1.63
LimGT 0.65-3.7 2.56s-3.7MB 6.93s- 3.7MB 650k:14.07-3.7 T/guard /lim
n^1.47 n^1.44 n^1.46
PT 0.48s-5.8MB 1.85s-7.8MB 5.08s-11.9MB 800k:13.90-20.1 Postponed T
n^1.44 n^1.46 n^1.45
ST 0.36s-5.8MB 1.38s-7.9MB 3.72s-12.0MB 14.04s-37.6MB Segmented T
n^1.44 n^1.43 n^1.45
------------------------------------------------------------------------------------------
LimGE 0.46-4.8 1.62s-4.8MB 4.35s- 4.8MB 900k:14.05-4.8 E/guard /lim
n^1.35 n^1.43 n^1.45
LimQE 0.39-4.8 1.46s-4.8MB 3.88s- 4.8MB 14.81s- 4.8MB E/sQ /lim
n^1.41 n^1.41 n^1.46
LimGQE 0.38-4.8 1.35s-4.8MB 3.59s- 4.8MB 13.79s- 4.8MB E/sQ /g /lim
n^1.36 n^1.41 n^1.47
PE 0.29s-6.8MB 1.12s-11.9MB 3.01s-22.1MB 11.60s-57.0MB Postponed E
n^1.44 n^1.43 n^1.47
------------------------------------------------------------------------------------------
LME 0.26s-4.8MB 0.95s- 4.8MB 2.52s- 4.8MB 9.33s- 4.8MB Linear Merge
n^1.39 n^1.41 n^1.43 2x feed
------------------------------------------------------------------------------------------
TME 0.16s-4.8MB 0.49s- 4.8MB 1.10s- 4.8MB 3.35s- 4.8MB 7.70s- 4.8MB Tree Merge
n^1.20 n^1.17 n^1.22 n^1.20
TMWE 0.07s-4.8MB 0.19s- 4.8MB 0.44s- 4.8MB 1.33s- 4.8MB 3.14s- 4.8MB TME w/Wheel
n^1.07 n^1.21 n^1.21 n^1.24
PQMWE 0.04s-3.8MB 0.14s- 3.8MB 0.32s- 3.8MB 0.98s- 4.8MB 2.25s- 4.8MB Join w/O'Neill's PQ
n^1.34 n^1.19 n^1.22 n^1.20
====================================================================================
2m 4m 6m 7m 8m
TME 7.70 - 4.8 3.4m:14.78-4.8
n^1.23
TMWE 3.14s- 4.8MB 7.44s- 4.8MB 12.23s- 4.8MB 14.81s-4.8MB
n^1.24 n^1.23 n^1.24
H8Y5V 2.74 - 4.8 6.23 - 4.8 10.14 - 4.8 12.06 - 4.8 14.20 - 4.8 ONeill original
n^1.19 n^1.20 n^1.12 n^1.22 ^1.19
PQMWE 2.25s- 4.8MB 5.19s- 4.8MB 8.48s- 4.8MB 10.18s-4.8MB 11.99s-4.8MB PQW w/ONeill-PQ
n^1.21 n^1.21 n^1.19 n^1.23 ^1.21
lKxjW 2.21 - 4.9 5.09 - 4.9 8.30 - 4.9 10.02 -4.9 11.79s-4.9MB imprvd ONeill sv
n^1.20 n^1.21 n^1.22 n^1.22 ^1.21
==========================================================================================-}
-- *) --- double-feed idea by Melissa O'Neill --- initial tree-folding idea by Dave Bayer
-- now THIS -- PQ-join instead of Tree-join:
-- (... ran and produced results OK on the FIRST TRY!!)
-- using MINUS instead of GAPS is BAD: 6.41 vs 5.21s for 4,000,000th prime
-- try separating out MULTS... YES! *BETTER!*
-- may try to fuse `gaps` with `joinPQ` -- see if it makes it faster .........
primesPQMWE = 2:3:5:7: gaps 11 wheel (joinPQ mults)
where -- sep mults:
primes' = 11:13: gaps 17 (drop 2 wheel) (joinPQ mults) -- 4m: NO CHANGE
mults = roll 11 wheel primes' -- 6m: 8.48s-4.8MB vs 8.53s-4.8
wheel = 2:4:2:4:6:2:6:4:2:4:6:6:2:6:4:2:6:4:6:8:4:2:4:2: -- 7m: 10.18s-4.8 vs 10.29s-4.8
4:8:6:4:6:2:4:6:2:6:6:4:2:4:6:2:6:4:2:4:2:10:2:10:wheel
gaps k ws@(w:t) cs@ ~(c:u) | k==c = gaps (k+w) t u -- (* PQ, 2x feed, w/ Wheel! *)
| True = k : gaps (k+w) t cs
roll k ws@(w:t) ps@ ~(p:u) | k==p = (p,ws) -- scanl (\c d->c+p*d) (p*p) ws -- with scanl, sep mults
: roll (k+w) t u -- is MUCH WORSE:
| True = roll (k+w) t ps -- slower and MEM shoots UP!
joinPQ ((p,ws) : sts@((p2,_):_) ) = go (singletonPQ (p*p) (p,ws)) (p2*p2) sts
where
go pq q sts = -- pq is good up to q, the 1st one in sts
let k1 = minKeyPQ pq -- next composite to produce
in if k1 < q
then k1 : go (next k1 pq) q sts
else
let (a : t@((p2,_):_) ) = sts
in go (insertPQ q a pq) (p2*p2) t
next k1 pq = -- k1 is the top -- get all equals off
let (c,(p,d:ws)) = minKeyValuePQ pq -- and reinsert with next values
in if c==k1
then next k1 $ deleteMinAndInsertPQ (c+p*d) (p,ws) pq
else pq
-- next step: fuse gaps + joinPQ ==> gapsPQ .. IN ERROR yet ..
primesFPQW = 2:3:5:7: gapsPQ 11 wheel mults
where
primes' = 11:13: gapsPQ 17 (drop 2 wheel) mults
mults = roll 11 wheel primes'
wheel = 2:4:2:4:6:2:6:4:2:4:6:6:2:6:4:2:6:4:6:8:4:2:4:2:
4:8:6:4:6:2:4:6:2:6:6:4:2:4:6:2:6:4:2:4:2:10:2:10:wheel -- (* PQ, 2x feed, w/ Wheel! *)
roll k ws@(w:t) ps@ ~(p:u) | k==p = (p,ws) : roll (k+w) t u
| True = roll (k+w) t ps
gapsPQ k0 ws0 ((p,ws) : sts@((p2,_):_) ) = go k0 ws0 (singletonPQ (p*p) (p,ws)) (p2*p2) sts
where
go k0 ws0@(w0:t0) pq q sts = -- pq is good up to q, the 1st one in sts
let k1 = minKeyPQ pq -- next composite to produce
in
if k0 < k1
then -- k0 is the next prime; return it without advancing the PQ
k0 : go (k0+w0) t0 pq q sts
else
-- if k0 == k1 -- is composite; ignore and advance as needed
-- then
if k1 < q
then -- k1 : go (next k1 pq) q sts
-- gaps k0 ws0 (k1 : go (next k1 pq) q sts)
go (k0+w0) t0 (next k1 pq) q sts
else
let (a : t@((p2,_):_) ) = sts
in go (k0+w0) t0 (insertPQ q a pq) (p2*p2) t
-- else -- k0 > k1
next k1 pq = -- k1 is the top -- get all equals off
let (c,(p,d:ws)) = minKeyValuePQ pq -- and reinsert with next values
in if c==k1
then next k1 $ deleteMinAndInsertPQ (c+p*d) (p,ws) pq
else pq
-- http://e...content-available-to-author-only...s.org/Priority_Queue_(Haskell) is almost TWICE SLOWER than O'Neill's PQ
-- O'Neill's PQ:
--------------------------------------------------------------------------------
-- ======== the file ONeillPrimes.hs ========
-- Generate Primes using ideas from The Sieve of Eratosthenes
--
-- This code is intended to be a faithful reproduction of the
-- Sieve of Eratosthenes, with the following change from the original
-- - The list of primes is infinite
-- (This change does have consequences for representing the number table
-- from which composites are "crossed out".)
--
-- (c) 2006-2007 Melissa O'Neill. Code may be used freely so long as
-- this copyright message is retained and changed versions of the file
-- are clearly marked.
----- // next line is commented out for Ideone.com testing (willness-2011-02-13) // ---
-- module ONeillPrimes (primes, sieve, calcPrimes, primesToNth, primesToLimit) where
-- Priority Queues; this is essentially a copy-and-paste-job of
-- PriorityQ.hs. By putting the code here, we allow the Haskell
-- compiler to do some whole-program optimization. (Based on ML
-- code by L. Paulson in _ML for the Working Programmer_.)
data PriorityQ k v = Lf -- ***********
| Br {-# UNPACK #-} !k v !(PriorityQ k v) !(PriorityQ k v)
emptyPQ :: PriorityQ k v
emptyPQ = Lf
isEmptyPQ
:: PriorityQ k v
-> BoolisEmptyPQ Lf = True
isEmptyPQ _ = False
minKeyValuePQ :: PriorityQ k v -> (k, v)
minKeyValuePQ (Br k v _ _) = (k,v)
minKeyValuePQ
_ = error "Empty heap!"
minKeyPQ :: PriorityQ k v -> k
minKeyPQ (Br k v _ _) = k
minKeyPQ
_ = error "Empty heap!"
minValuePQ :: PriorityQ k v -> v
minValuePQ (Br k v _ _) = v
minValuePQ
_ = error "Empty heap!"
insertPQ
:: Ord k
=> k
-> v
-> PriorityQ k v
-> PriorityQ k v
insertPQ wk wv (Br vk vv t1 t2)
| wk <= vk = Br wk wv (insertPQ vk vv t2) t1
| otherwise = Br vk vv
(insertPQ wk wv t2
) t1
insertPQ wk wv Lf = Br wk wv Lf Lf
siftdown
:: Ord k
=> k
-> v
-> PriorityQ k v
-> PriorityQ k v
-> PriorityQ k v
siftdown wk wv Lf _ = Br wk wv Lf Lf
siftdown wk wv (t @ (Br vk vv _ _)) Lf
| wk <= vk = Br wk wv t Lf
siftdown wk wv (t1 @ (Br vk1 vv1 p1 q1)) (t2 @ (Br vk2 vv2 p2 q2))
| wk <= vk1 && wk <= vk2 = Br wk wv t1 t2
| vk1 <= vk2 = Br vk1 vv1 (siftdown wk wv p1 q1) t2
| otherwise = Br vk2 vv2 t1
(siftdown wk wv p2 q2
)
deleteMinAndInsertPQ
:: Ord k
=> k
-> v
-> PriorityQ k v
-> PriorityQ k v
deleteMinAndInsertPQ wk wv Lf
= error "Empty PriorityQ"deleteMinAndInsertPQ wk wv (Br _ _ t1 t2) = siftdown wk wv t1 t2
leftrem :: PriorityQ k v -> (k, v, PriorityQ k v)
leftrem (Br vk vv Lf Lf) = (vk, vv, Lf)
leftrem (Br vk vv t1 t2) = (wk, wv, Br vk vv t t2) where
(wk, wv, t) = leftrem t1
leftrem
_ = error "Empty heap!"
deleteMinPQ
:: Ord k
=> PriorityQ k v
-> PriorityQ k v
deleteMinPQ (Br vk vv Lf _) = Lf
deleteMinPQ (Br vk vv t1 t2) = siftdown wk wv t2 t where
(wk,wv,t) = leftrem t1
deleteMinPQ
_ = error "Empty heap!"
----- // the rest of the file is omitted here (willness-2011-05-20) // ---
----- an interface function (willness-2011-05-20) --
singletonPQ k v = insertPQ k v emptyPQ