0
\$\begingroup\$

The program works, but is extremely slow.

Project Euler problem 3

Problem: What is the largest prime factor of the number 600851475143?

module Problem_3 where

-- find largest prime factor:
findLargestPrimeFactor :: [Int] -> Int
findLargestPrimeFactor []     = 1
findLargestPrimeFactor (n:ns) = 
    if (isPrime n)
    then n
    else findLargestPrimeFactor ns


-- Supplementary function definitions:

-- function: listOfFactors n
listOfFactors :: Int -> [Int]
listOfFactors n = [x | x <- [1..n], isFactor x]
    where
        isFactor x = (n `mod` x == 0)

-- function: isPrime p
isPrime :: Int -> Bool
isPrime p = (p >= 2 && listOfFactors p == [1,p]) 

main = do 
          let myGivenNumber = 600851475143
          let largestPrimeFactor = findLargestPrimeFactor (reverse (listOfFactors myGivenNumber))
          print largestPrimeFactor
\$\endgroup\$
7
  • \$\begingroup\$ I'm afraid this question does not match what this site is about. Code Review is about improving existing, working code. Code Review is not the site to ask for help in fixing or changing what your code does. Once the code does what you want, we would love to help you do the same thing in a cleaner way! Please see our help center for more information. \$\endgroup\$
    – Zeta
    Commented May 1, 2017 at 15:13
  • \$\begingroup\$ Where do I get that sort of help then? \$\endgroup\$
    – UGPhysics
    Commented May 1, 2017 at 15:46
  • \$\begingroup\$ Your code does not work? StackOverflow. Your code does work and you want to know what you can do better? Here. But at the moment, your code does not even type check, therefore it's off-topic at the moment. Also, add more information. Just the problem statement and code isn't really a well written description. \$\endgroup\$
    – Zeta
    Commented May 1, 2017 at 15:48
  • \$\begingroup\$ - Ask Moderator/s to send to SO? \$\endgroup\$
    – UGPhysics
    Commented May 1, 2017 at 15:51
  • 1
    \$\begingroup\$ @UGPhysics No need for moderators, you can just delete your question here and reask it at SO. \$\endgroup\$ Commented May 1, 2017 at 16:31

1 Answer 1

1
\$\begingroup\$

Naming

findLargestPrimeFactor isn't true to its name. It just returns the first prime number in the given list or 1 otherwise. So firstPrime :: [Int] -> Int might be more apt.

Efficency

At the moment, you check \$ n \$ candidates in listOfFactors. But that's not necessary. First of all, if we found a divisor x, then we also found a second divisor, n `div` x. We also only need to check x up to \$ \sqrt{n}\$:

listOfFactors :: Int -> [Int]
listOfFactors n = concat [[x,d] | x <- [1..nsqrt], let (d,m) = n `divMod` x, m == 0]
  where
    nsqrt = ceiling (sqrt (fromIntegral n))

Now we only need to check \$\left\lceil \sqrt{600851475143}\right\rceil = 775147\$ numbers, which is much, much less. This variant will actually finish in under one second, whereas I didn't run my program till the end.

However, you now have to sort your list, or use filter isPrime followed by maximum in findLargestPrimeFactor, whose name would be then mostly (beside the Factor) apt.

If you rewrite findLargestPrime so that it returns the largest prime from a given list, we would end up with the following main:

main :: IO ()
main = print (findLargestPrime (listOfFactors 600851475143))

Which seems reasonable.

Another approach

While one can solve this problem in this way, one can also just generate the prime factors of the number:

primeFactors :: Int -> [Int]
primeFactors n = go 2 n
  where
    go _ 1 = []
    go k n = case n `quotRem` k of
               (n', 0) -> k : go k n'
               _       ->     go (k + 1) n'

If k is going to be put in the list, it will always be prime. Note that one can improve this function further, but that's left as an exercise.

Exercises

  • In the section "efficency", we were able to reduce the runtime by narrowing the search space. However, why is it enough to run up to $\ \sqrt{n} \$ only?
  • In the section "another approach", one can cut the amount of checked numbers almost by half. How?
\$\endgroup\$
6
  • \$\begingroup\$ what is the answer for the question then? \$\endgroup\$
    – UGPhysics
    Commented May 2, 2017 at 2:05
  • \$\begingroup\$ @UGPhysics for what question? The one given by Project Euler? \$\endgroup\$
    – Zeta
    Commented May 2, 2017 at 5:28
  • \$\begingroup\$ ceil in nsqrt = ceil (sqrt (fromIntegral n)) returns error: Variable not in scope: ceil :: Double -> t \$\endgroup\$
    – UGPhysics
    Commented May 2, 2017 at 5:43
  • \$\begingroup\$ @UGPhysics typo, meant ceiling (which is called ceil in the other languages I usually use), sorry. \$\endgroup\$
    – Zeta
    Commented May 2, 2017 at 5:46
  • \$\begingroup\$ listOfFactors n = foldr (.) id [((d:) .) . (. (x:)) | x <- [1..nsqrt], let (d,m) = n `divMod` x, m == 0] id [] puts the factors in descending order immediately. \$\endgroup\$
    – Gurkenglas
    Commented May 2, 2017 at 17:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.