Cualquiera dirá hallar el numero primo es fácil, si es verdad de echo ya hay algoritmos que te calculan los números primos, pero para un rango de mil millones la cosa se complica.
El reto esta en generarlo en el menor tiempo posible en el lenguaje funcional Haskell.
Mi docente me explico un poco como funciona la criba de Erastotenes, entonces decidí aplicar ese algoritmo para generar los números primos.
Este es mi primer intento para 10 millones de números:
{-====================================================================================== | |
Autor : to_On1 | |
Email : antonio.mq@gmail.com | |
Versión : 0.1 | |
Descripción : Aplicación del algoritmos de Erastotenes | |
=======================================================================================-} | |
main :: IO() | |
main = writeFile "primosErastotenes" $ unlines $ erastotenes [2..10000000] | |
erastotenes :: [Integer] -> [String] | |
erastotenes [] = [] | |
erastotenes (x:xs) | x^2 > last xs = show x : map show xs | |
| otherwise = show x : erastotenes (filter (\ y -> y `mod` x/= 0 ) xs) |
Y obtuve los siguientes resultados
$ time ./Primes
real 1m8.407s
user 1m5.015s
sys 0m0.815s
Con mi código, de entrada no podre superar el reto, entonces me puse a analizar por que tardaba tanto y como podía mejorarlo.
Pude hacer la siguiente mejora para mil millones de números:
{-================================================================================== | |
Autor : to_On1 | |
Email : antonio.mq@gmail.com | |
Version : 0.2 | |
Descripcion : Este algoritmo (mejorado) es una modificacion del algoritmo de | |
Erastotenes,donde armo una lista discriminando los números | |
multiplos de 2,3,..47, esto reduce a menos de la mitad la lista que sera | |
evaluada. | |
===================================================================================-} | |
main :: IO() | |
main = writeFile "primes" $ unlines $ res ++ (primos $ lista 53) | |
where | |
res = "2":"3":"5":"7":"11":"13":"17":"19":"23":"29":"31":"37":"41":"43":"47":[] | |
-- Creo la lista donde mi valor final sera 999999999 esto es devido a que | |
-- mil millones es numero par | |
lista :: Integer -> [Integer] | |
lista 999999999 = [] | |
lista x | x `mod` 3 == 0 = lista $ x+2 | |
| x `mod` 5 == 0 = lista $ x+2 | |
| x `mod` 7 == 0 = lista $ x+2 | |
| x `mod` 11 == 0 = lista $ x+2 | |
| x `mod` 13 == 0 = lista $ x+2 | |
| x `mod` 17 == 0 = lista $ x+2 | |
| x `mod` 19 == 0 = lista $ x+2 | |
| x `mod` 23 == 0 = lista $ x+2 | |
| x `mod` 29 == 0 = lista $ x+2 | |
| x `mod` 31 == 0 = lista $ x+2 | |
| x `mod` 37 == 0 = lista $ x+2 | |
| x `mod` 41 == 0 = lista $ x+2 | |
| x `mod` 43 == 0 = lista $ x+2 | |
| x `mod` 47 == 0 = lista $ x+2 | |
| otherwise = x : lista (x+2) | |
primos :: [Integer] -> [String] | |
primos [] = [] | |
primos (x:xs) | x < floor (sqrt 1000000000)+1 = show x : map show xs | |
| otherwise = show x : primos (filter (\ y -> y `mod` x /= 0) xs ) |
$time ./Primos
real 4m59.920s
user 4m40.015s
sys 0m6.015s
Si hacemos una comparación con mi primera versión, logre mejorar bastante ya que para el primero solo calculo para diez millones y para el segundo calculo para mil millones números.
Haciendo cálculos matemáticos 100 * 5 (redondeado al tiempo que tardo para mil millones) = 500 minutos y lo dividimos en 60 minutos, nos dará el siguiente resultado 500/60 = 8.33.
Esto quiere decir que para calcular el rango de mil millones de números tardara 8 horas con 33 minutos.