[Haskell-cafe] faster factorial function via FFI?
Dan Weston
westondan at imageworks.com
Mon Apr 23 23:02:24 EDT 2007
Why all the fuss? n! is in fact very easily *completely* factored into
prime numbers (and this never changes, so you can just read in a binary
lookup table). It took my machine only 3 seconds to find the prime
factors of the first 1000 factorials:
Then you can do for example:
import List
binomCoeffFactors :: Int -> Int -> [Int]
binomCoeffFactors n k = let pfs = primeFactorials in
(pfs !! n \\ pfs !! k)
\\ pfs !! (n-k)
binomCoeff :: Int -> Int -> Int
binomCoeff n k = foldr (*) 1 (binomCoeffFactors n k)
-- List of primes. This is reasonably fast but doesn't have to be,
-- since we only need it this once to generate the lookup table
primes = (2 : [x | x <- [3,5..], isPrime x])
-- Efficient test presupposes the existence of primes
-- This works because to determine whether p is prime you only need
-- to know the primes strictly less than p (except for 2 of course!)
isPrime x = null divisors
where divisors = [y | y <- onlyUpToSqrtX primes, x `mod` y == 0]
onlyUpToSqrtX = fst . span (<= sqrtX)
sqrtX = floor (sqrt (fromIntegral x))
primeFactor' :: [Int] -> Int -> [Int]
primeFactor' p@(h:t) n
| n < 2 = []
| otherwise = let (d,m) = n `divMod` h in
if m == 0 then h : primeFactor' p d
else primeFactor' t n
primeFactors :: Int -> [Int]
primeFactors = let p = primes in primeFactor' p
primeFactorial' :: (Int, [Int]) -> (Int, [Int])
primeFactorial' (n,pfs) = (n1,ret)
where pfn = primeFactors n1
ret = merge pfn pfs
n1 = n + 1
primeFactorials :: [[Int]]
primeFactorials = map snd . iterate primeFactorial' $ (0,[])
-- timingTest 10000 takes only 3 seconds!
timingTest :: Int -> Int
timingTest nMax = let pfs = primeFactorials in
sum . map sum . take nMax $ pfs
-- PRE : Args are sorted
-- POST: Result is sorted union
merge as [] = as
merge [] bs = bs
merge aas@(a:as) bbs@(b:bs) | a < b = a : merge as bbs
| otherwise = b : merge aas bs
More information about the Haskell-Cafe
mailing list