Here is another prime sieve. It is about half the length of the
fastest contributed code (ONeillPrimes) and nearly as fast until it
blows up on garbage collection:
% cat ONeillPrimes.hs | grep -v "^--" | wc
185 1105 6306
% cat BayerPrimes.hs | grep -v "^--" | wc
85 566 2418
[Integer] -O 1*10^6 2*10^6 3*10^6 4*10^6 5*10^6
---------------------------------------------------------
ONeillPrimes | 3.555 | 7.798 | 12.622 | 18.927 | 23.529
BayerPrimes | 3.999 | 8.895 | 18.003 | 22.977 | 38.053
I wrote this as an experiment in coding data structures in Haskell's
lazy evaluation model, rather than as explicit data. The majority of
the work done by this code is done by "merge"; the multiples of each
prime percolate up through a tournament consisting of a balanced tree
of suspended "merge" function calls. In order to create an infinite
lazy balanced tree of lists, the datatype
data List a = A a (List a) | B [a]
is used as scaffolding. One thinks of the root of the infinite tree
as starting at the leftmost child, and crawling up the left spine as
necessary.
What I'm calling a "venturi"
venturi :: Ord a => [[a]] -> [a]
merges an infinite list of infinite lists into one list, under the
assumption that each list, and the heads of the lists, are in
increasing order. This could be a generally useful function. If one
can think of a better way to write "venturi", swapping in your code
would in particular yield a faster prime sieve.
I found that a tertiary merge tree was faster than a binary merge
tree, because this leads to fewer suspensions. One can speed this
code up a bit by interleaving strict and lazy calls, but I prefer to
leave the code short and readable.
It is bizarre that
trim p = let f m x = mod x m /= 0 in filter (f p)
lurks in the prime sieve code, but it is only used with primes of
size up to the square root of the largest output prime. I tried more
thoughtful alternatives, and they all slowed down the sieve.
Sometimes dumb is beautiful.
Thanks to apfelmus for various helpful remarks that lead me to think
of this approach.
Here is the code:
module BayerPrimes (venturi,primes) where
-- Code for venturi :: Ord a => [[a]] -> [a]
merge :: Ord a => [a] -> [a] -> [a] -> [a]
merge xs@(x:xt) ys@(y:yt) zs@(z:zt)
| x <= y = if x <= z
then x : (merge xt ys zs)
else z : (merge xs ys zt)
| otherwise = if y <= z
then y : (merge xs yt zs)
else z : (merge xs ys zt)
merge _ _ _ = undefined
data List a = A a (List a) | B [a]
mergeA :: Ord a => List a -> List a -> List a -> List a
mergeA (A x xt) ys zs = A x (mergeA xt ys zs)
mergeA (B xs) ys zs = mergeB xs ys zs
mergeB :: Ord a => [a] -> List a -> List a -> List a
mergeB xs@(x:xt) ys@(A y yt) zs = case compare x y of
LT -> A x (mergeB xt ys zs)
EQ -> A x (mergeB xt yt zs)
GT -> A y (mergeB xs yt zs)
mergeB xs (B ys) zs = mergeC xs ys zs
mergeB _ _ _ = undefined
mergeC :: Ord a => [a] -> [a] -> List a -> List a
mergeC xs@(x:xt) ys@(y:yt) zs@(A z zt)
| x < y = if x < z
then A x (mergeC xt ys zs)
else A z (mergeC xs ys zt)
| otherwise = if y < z
then A y (mergeC xs yt zs)
else A z (mergeC xs ys zt)
mergeC xs ys (B zs) = B $ merge xs ys zs
mergeC _ _ _ = undefined
root :: Ord a => List a -> [List a] -> [a]
root (A x xt) yss = x : (root xt yss)
root (B xs) (ys:zs:yst) = root (mergeB xs ys zs) yst
root _ _ = undefined
wrap :: [a] -> List a
wrap [] = B []
wrap (x:xt) = A x $ B xt
triple :: Ord a => [List a] -> [List a]
triple (x:y:z:xs) = mergeA x y z : (triple xs)
triple _ = undefined
group :: Ord a => [List a] -> [List a]
group (x:y:xt) = x : y : (group $ triple xt)
group _ = undefined
venturi :: Ord a => [[a]] -> [a]
venturi (x:xt) = root (wrap x) $ group $ map wrap xt
venturi _ = undefined
-- Code for primes :: Integral a => [a]
diff :: Ord a => [a] -> [a] -> [a]
diff xs@(x:xt) ys@(y:yt) = case compare x y of
LT -> x : (diff xt ys)
EQ -> (diff xt yt)
GT -> (diff xs yt)
diff _ _ = undefined
trim :: Integral a => a -> [a] -> [a]
trim p = let f m x = mod x m /= 0 in filter (f p)
seed :: Integral a => [a]
seed = [2,3,5,7,11,13,17]
wheel :: Integral a => [a]
wheel = drop 1 [ m*j + k | j <- [0..], k <- ws ]
where m = foldr1 (*) seed
ws = foldr trim [1..m] seed
multiples :: Integral a => [a] -> [[a]]
multiples ws = map fst $ tail $ iterate g ([], ws)
where g (_,ps@(p:pt)) = ([ m*p | m <- ps ], trim p pt)
g _ = undefined
primes :: Integral a => [a]
primes = seed ++ (diff wheel $ venturi $ multiples wheel)
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe