[Haskell-cafe] Zumkeller numbers
Daniel Fischer
daniel.is.fischer at web.de
Mon Dec 7 20:22:59 EST 2009
Am Dienstag 08 Dezember 2009 01:54:12 schrieb ajb at spamcop.net:
> 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:
<snip>
>
> Yes, I cheated by using a different algorithm.
>
> Cheers,
> Andrew Bromage
Yes, what I do (after fixing an embarrassing typo from the first post). Using Int, it's a
little faster, with Integer the same time. I've now added the printout of one partition.
module Main (main) where
import Data.List (sort)
import Control.Monad (liftM2)
import System.Environment (getArgs)
main = do
args <- getArgs
let bd = case args of
(a:_) -> read a
_ -> 4000
mapM_ (\n -> case zumParts n of
[] -> return ()
((l,r):_) -> putStrLn (show n ++ " " ++ show l ++ " " ++ show r))
[1 .. bd]
trialDivPrime :: [Int] -> Int -> Bool
trialDivPrime (p:ps) n = (p*p > n) || (n `mod` p /= 0) && trialDivPrime ps n
oprs = 5:7:filter (trialDivPrime oprs) (scanl (+) 11 $ cycle [2,4])
primes :: [Int]
primes = 2:3:oprs
factors :: Int -> [(Int,Int)]
factors n
| n < 0 = factors (-n)
| n == 0 = [(0,1)]
| otherwise = go n primes
where
go 1 _ = []
go m (p:ps)
| p*p > m = [(m,1)]
| otherwise =
case m `quotRem` p of
(q,0) -> case countFactors q p of
(m',k) -> (p,k+1):go m' ps
_ -> go m ps
countFactors :: Int -> Int -> (Int,Int)
countFactors n p = go n 0
where
go m k = case m `quotRem` p of
(q,0) -> go q (k+1)
_ -> (m,k)
divisors :: Int -> [Int]
divisors n = sort $ foldr (liftM2 (*)) [1] ppds
where
ppds = map (\(p,k) -> take (k+1) $ iterate (*p) 1) $ factors n
partition :: Int -> [Int] -> [[Int]]
partition 0 _ = [[]]
partition n [] = []
partition n (p:ps)
| n < p = partition n ps
| otherwise = [p:parts | parts <- partition (n-p) ps] ++ partition n ps
zumParts :: Int -> [([Int],[Int])]
zumParts n
| sigma < 2*n = []
| odd sigma = []
| otherwise = [(prt ++ [n],init divs `without` prt) | prt <- prts]
where
divs = divisors n
sigma = sum divs
target = (sigma-2*n) `quot` 2
sml = takeWhile (<= target) $ init divs
prts = map reverse $ partition target (reverse sml)
xxs@(x:xs) `without` yys@(y:ys)
| x < y = x:xs `without` yys
| x == y = xs `without` ys
| otherwise = xxs `without` ys
xs `without` _ = xs
More information about the Haskell-Cafe
mailing list