¿Cómo escribir esto como una sola función?

I'm having a go at project Euler Q3 and need to get the largest prime factor of a number. So far I've gotten a pair of functions to return a list of all the factors of the given number but it seems like a really bad way to do it (partly because I only need the largest).

get_factors :: (Integral a) => a -> [a] -> [a]
get_factors _ [] = []
get_factors t (x:xs)
    | t `mod` x == 0 = x:get_factors t xs
    | otherwise = get_factors t xs

factors :: (Integral a) => a -> [a]
factors x = get_factors x [x,x-1..1]

> factors 1000
> [1000,500,250,200,125,100,50,40,25,20,10,8,5,4,2,1]

It seems weird to me that I would need to have a "launch" function if you will to start the recursive function off (or have a function where I have to pass it the same value twice, again, seems silly to me).

Can you point me in the right direction of how I should be going about doing this please?

preguntado el 05 de mayo de 13 a las 18:05

I just want to point out that if y is the smallest prime factor of x, entonces x `div` y is the largest one. You may be able to save some computation here. If you use a fast implementation of the Eratosthenes' sieve instead of the linear trial divisors list, you may be able to save some more. -

@Nuevo Méjico x `div` y need not be prime. -

@hammar Yes, just realized that after hitting enter... OTOH the presented procedure does not produce prime factors either. -

I figured I'd create factors first then filter them based on primeness. I wanted to break the problem down. -

3 Respuestas

You should try to recognize that what you're doing here, namely picking elements from a list which satisfy some condition, is a very common pattern. This pattern is implemented by the filter function in the Prelude.

Usar filter, you can write your function as:

factors n = filter (\d -> n `mod` d == 0) [n, n-1 .. 1]

or, equivalently, you can use a list comprehension:

factors n = [d | d <- [n, n-1 .. 1], n `mod` d == 0]

contestado el 05 de mayo de 13 a las 18:05

Using a "launch" function for calling a recursive function is very common in Haskell, so don't be afraid of that. Most often it'd be written as

f = g someArgument
  where
    g = ...

en tu caso

factors :: (Integral a) => a -> [a]
factors x = get_factors [x,x-1..1]
  where
    get_factors [] = []
    get_factors (y:ys)
        | x `mod` y == 0   = y : get_factors ys
        | otherwise        = get_factors ys

This signals readers of your code that get_factors is used only here and nowhere else, and helps you to keep the code clean. Also get_factors tiene acceso a x, which simplifies the design.


Algunas otras ideas:

  • It's inefficient to try dividing by all numbers. In problems like that it's much better to pre-compute the list of primes and factor using the list. There are muchos métodos how to compute such a list, but for educational purposes I'd suggest you to write your own (this will come in handy for other Project Euler problems). Then you could take the list of primes, take a part of primes less or equal than x and try dividing by them.
  • When searching just for the largest factor, you have to search through all primes between 1 and x. Pero si x is composite, one of its factors must be <= sqrt(n). You can use this to construct a significantly better algorithm.

contestado el 05 de mayo de 13 a las 18:05

I do not think it is a very good idea to go through every number like [n, n-1..] since the problem says 600851475143.

largest_factors :: Integer -> Integer
largest_factors n = helper n 2
    where
        helper m p  
            | m < p^2 = m
            | m == p = m
            | m `mod` p == 0 = helper (m `div` p) p
            | otherwise = helper m (p+1)

What I did is that, once it found that a certain number, say p, divides the number n, it just divides it. This one works on my computer just fine. This gave me the solution within a sec.

contestado el 05 de mayo de 13 a las 21:05

This works well in this case because the input number happens to have low prime factors. One simple improvement you can make is that once p*p > m, m must be prime, so you can stop there instead of continuing all the way to m == p. - Hammar

@hammar Thank you for your suggestion!! - tengu

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.