Project Euler - Problem 12

Project Euler defines problem 12 as:

What is the value of the first triangle number to have over five hundred divisors?

My brute force solution took a couple of seconds (compiled with -O3), but trying to come up with a neater solution, I came across the fact that the sum of the exponents, each incremented by one gives the number of factors. E.g. The prime factors of 108 are 22, 33. Therefore the number of factors of 108 = (2+1)(3+1) = 12

Unfortunately, after implementing this, I only gained about 200ms (10%) on the previous version. I am fairy sure there are some better optimizations that I could have done.

  Original Solution:

sumTo :: Int -> Int
sumTo n = (n*(n+1))`div`2

numFactors :: Int -> Int
numFactors num =  ( doNumFactors num $ sqrtInt num ) * 2
		where
			sqrtInt :: Int -> Int
			sqrtInt n = floor ( sqrt ( fromIntegral n ) ) 

			doNumFactors :: Int -> Int -> Int
			doNumFactors num 0 		= 0
			doNumFactors num current = (iIsFactor num current)  + (doNumFactors num $ current - 1)
			
			iIsFactor :: Int -> Int -> Int
			iIsFactor n f = fromEnum ((n `mod` f) == 0)
--Like Data.list.find, but it doesn't use maybe. This doesn't matter as we are using lists [1..∞]
takeFirstWhere :: (a -> Bool) -> [a] -> a takeFirstWhere f (x:xs) | f(x) = x | otherwise = takeFirstWhere f xs solve :: Int -> Int solve limit = takeFirstWhere (\x -> ( numFactors $ x ) > limit ) [ sumTo x | x <- [1..] ]

 

 

Updated numFactors function:

numFactors :: Int -> Int
numFactors n = doCalc $ getListCounts $ getPrimeFactors n
		where
			doCalc l = foldl1 (*) $ map (+1) l
--Returns an array of counts of every value in the given list
--So getListCounts [2,2,2,1] == [3,1]
getListCounts :: [Int] -> [Int]
getListCounts [] = [] getListCounts l = countWhere (== ( head l ) ) l : ( getListCounts $ filter(/= (head l) ) l )

countWhere :: ( a -> Bool) -> [a] -> Int countWhere f l = foldl (\x y -> x + (fromEnum $ f y) ) 0 l getPrimeFactors :: Int -> [Int] getPrimeFactors 1 = [] getPrimeFactors n = prime : ( getPrimeFactors $ n `div` prime ) where prime :: Int prime = takeFirstWhere (\x -> n `mod` x == 0 ) [2..]