[Haskell-cafe] Zumkeller numbers
ajb at spamcop.net
ajb at spamcop.net
Mon Dec 7 19:54:12 EST 2009
G'day all.
Quoting Richard O'Keefe <ok at cs.otago.ac.nz>:
> These lines of Haskell code find the Zumkeller numbers up to 5000
> in 5 seconds on a 2.2GHz intel Mac. The equivalent in SML took
> 1.1 seconds. Note that this just finds whether a suitable
> partition exists; it does not report the partition.
This takes 0.1 seconds on a 2GHz Dell running Linux:
module Main where
import Control.Monad
import qualified Data.List as L
primesUpTo :: Integer -> [Integer]
primesUpTo n
| n < 13 = takeWhile (<= n) [2,3,5,11]
| otherwise = takeWhile (<= n) primes
where
primes = 2 : 3 : sieve 0 (tail primes) 5
sieve k (p:ps) x
= let fs = take k (tail primes)
in [x | x<-[x,x+2..p*p-2], and [x `rem` p /= 0 | p<-fs] ]
++ sieve (k+1) ps (p*p+2)
pseudoPrimesUpTo :: Integer -> [Integer]
pseudoPrimesUpTo n
| n <= wheelModulus = takeWhile (<= n) smallPrimes
| otherwise = smallPrimes ++ pseudoPrimesUpTo' wheelModulus
where
largestSmallPrime = 11
verySmallPrimes = primesUpTo largestSmallPrime
wheelModulus = product verySmallPrimes
smallPrimes = primesUpTo wheelModulus
wheel = mkWheel 1 [1] verySmallPrimes
mkWheel _ rs [] = rs
mkWheel n rs (p:ps) = mkWheel (p*n) (do
k <- [0..p-1]
r <- rs
let r' = n*k + r
guard (r' `mod` p /= 0)
return r') ps
pseudoPrimesUpTo' base
| n <= base + wheelModulus
= takeWhile (<= n) . map (+base) $ wheel
| otherwise
= map (+base) wheel ++ pseudoPrimesUpTo' (base + wheelModulus)
-- Integer square root.
isqrt :: Integer -> Integer
isqrt n
| n < 0 = error "isqrt"
| otherwise = isqrt' ((n+1) `div` 2)
where
isqrt' s
| s*s <= n && n < (s+1)*(s+1) = s
| otherwise = isqrt' ((s + (n `div` s)) `div` 2)
-- Compute the prime factorisation of an integer.
factor :: Integer -> [Integer]
factor n
| n <= 0 = error "factor"
| n <= primeCutoff = factor' n (primesUpTo primeCutoff)
| otherwise = factor' n (pseudoPrimesUpTo (isqrt n))
where
primeCutoff = 1000000
factor' 1 _ = []
factor' n [] = [n]
factor' n (p:ps)
| n `mod` p == 0 = p : factor' (n `div` p) (p:ps)
| otherwise = factor' n ps
-- The only function used from above is "factor".
zumkeller :: Integer -> Bool
zumkeller n
| isPrime = False
| isPrime = False
| sigma `mod` 2 == 1 = False
| sigma < 2*n = False
| otherwise = sigmaTest ((sigma - 2*n) `div` 2) (factors n)
where
isPrime = case factorn of
[_] -> True
_ -> False
factorn = factor n
sigma = product . map (sum . scanl (*) 1) . L.group $ factorn
factors n = reverse [ f | f <- [1..n `div` 2], n `mod` f == 0 ]
sigmaTest 0 _ = True
sigmaTest _ [] = False
sigmaTest n (f:fs)
| f > n = sigmaTest n fs
| otherwise = sigmaTest (n-f) fs || sigmaTest n fs
main = print (filter zumkeller [1..5000])
Yes, I cheated by using a different algorithm.
Cheers,
Andrew Bromage
More information about the Haskell-Cafe
mailing list