¿Cuál sería un algoritmo apropiado para factorizar números en el rango de unos pocos miles de millones?

8

Estoy aprendiendo Python en este momento y para darme razones para aplicar lo que estoy aprendiendo, estoy teniendo problemas con algunos de los problemas del Proyecto Euler.

Actualmente estoy en el número 3, que es determinar el factor primo más alto de dicho número.

He deducido que probablemente necesito tener dos algoritmos, uno para determinar la originalidad y el segundo, que implicaría encontrar factores del número.

Así que he estado leyendo sobre artículos de Wiki . Tratando de determinar cuál podría ser el mejor algoritmo para usar y cómo hacerlo.

Pero ha pasado un tiempo desde que hice una programación basada en matemáticas y estoy luchando por comenzar en alguna parte.

Estaba buscando usar el método de factorización de Fermat con la inclusión de Trial by Division, pero no quiero hacer algo demasiado complicado. No busco descifrar RSA. Solo quiero dos algoritmos adecuados para mi problema y ahí está mi pregunta.

¿Qué algoritmos usaría para probar la primalidad / factorización de un número que sea adecuado para el problema en cuestión?

Editar

Gracias a todos por sus respuestas y puntos de vista, han sido de gran ayuda. Voté todos los que fueron útiles, ya sea a través de consejos o de sus propias experiencias con Euler. El que marqué como correcto fue simplemente el más útil, ya que me dio un lugar adecuado para comenzar, que fue un empujón en la dirección correcta. Gracias de nuevo =)

Chris
fuente
Tales problemas pueden utilizar mejor el procesamiento paralelo.
NoChance
Tal vez tenga razón en general, pero para el proyecto euler suele ser más importante encontrar un algoritmo "inteligente". Son mucho más rápidos que los enfoques de fuerza bruta paralelos.
sebastiangeiger
Este es un problema matemáticamente difícil y no encontrará una solución ideal .
DeadMG

Respuestas:

5

Mi enfoque para esos problemas suele ser este: construir el algoritmo más simple posible para resolverlo, que generalmente es un enfoque ingenuo de fuerza bruta, y luego probar / calcular matemáticamente si es demasiado lento o no. La mayoría de las veces es lo suficientemente bueno. Cuando no es así, tiene un punto de partida claro para trabajar y optimizar las cosas hasta que el algoritmo sea lo suficientemente eficiente.

Aquí hay un algoritmo simple para resolver el problema 3 en el Proyecto Euler (en C, pero traducirlo a Python debería ser trivial):

#include <stdio.h>
#include <math.h>

int isPrime(int n){
    int i;

    if (n==2)
        return 1;

    if (n%2==0)
        return 0;
    for (i=3;i<sqrt(n);i+=2)
        if (n%i==0)
            return 0;
    return 1;
}

int main(){
    long long int n = 600851475143;
    int i = 3;

    while (i<50000){
        if (isPrime(i))
            if (n%i==0)
                printf("%d\n",i);
        i+=2;
    }
    return 0;
}
Daniel Scocco
fuente
1
Yo diría que usar isPrimees una exageración. Simplemente hacer n/=2while n%2==0y luego comenzar icon 3 y luego hacer un bucle if (n%i==0) n/=i; else i+=2;es suficiente (bueno, se puede detener una vez i*i > n).
herby
1
Mi experiencia en la resolución del proyecto euler es que este enfoque está funcionando para los problemas anteriores, pero probablemente tenga que refinarlo cuando resuelva problemas más complicados.
sebastiangeiger
@Sebastian, estoy en el problema 73, y sí, en el camino, los enfoques más ingenuos dejan de funcionar. Pero oye, ¿por qué complicar las cosas antes de que las necesites?
Daniel Scocco
2
@daniels: Puedo estar siendo tonto aquí. ¿Cómo resuelve esto el problema? a) 50000 parece terriblemente arbitrario. b) Está imprimiendo todos los factores primos, no solo los más altos. c) Verificar si es primo antes de verificar si es un factor parece un desperdicio. d) 2 es un número primo.
pdr
@pdr, a) los factores primos no suben tan rápido, así que pensé que 50000 sería lo suficientemente grande. Si no fuera así, siempre podría repetir con 100000 o sqrt (n). b) correcto, lo que significa que solo necesita mirar el último impreso (imprimí todo solo para ver qué estaba pasando). c) Estoy de acuerdo en que invertir las pruebas sería más eficiente, pero no hace ninguna diferencia en este caso (es decir, ganaría algunos milisegundos) d) sí, olvidé ese caso especial en mi función isPrime ().
Daniel Scocco
4

Vale la pena escribir un código que fabrique y fabrique (básicamente lo mismo) porque probablemente lo reutilizará en muchas otras preguntas de Euler. Podrá mejorar el código para preguntas posteriores y tal vez buscar pruebas de primitivas no exhaustivas si considera que ya no es lo suficientemente eficiente, por lo que sugiero que el enfoque más simple por ahora es:

  • Escriba un bucle simple que encuentre todos los números primos (es decir, para cada número, pruebe su divisibilidad por cada primo encontrado previamente y, si todos fallan, agréguelo a la lista de primos).
  • Intenta dividir el número que intentas factorizar por cada primo hasta la raíz cuadrada del número.
Jack V.
fuente
4

En realidad, esta es un área de investigación activa en Matemáticas e Informática. El artículo de wikipedia ofrece una buena descripción general:

http://en.wikipedia.org/wiki/Integer_factorization

Elija cualquier algoritmo que le guste / encuentre interesante, y pruebe.

Probablemente tendrá que hacer una compensación: la mayoría de los algoritmos "buenos" requieren un poco de experiencia matemática para comprender realmente (aunque podría implementarlos sin comprenderlos por completo).

Si no sabe por dónde empezar, le recomiendo el tamiz cuadrático:

http://en.wikipedia.org/wiki/Quadratic_sieve

No requiere un loco conocimiento matemático, pero funciona bien.

sleske
fuente
2

Resolví algunos problemas de ProjectEuler hace algún tiempo en Ruby usando la división de prueba con números primos .

Descubrí que generar los números primos era mucho más crítico que el algoritmo de factorización real. Tan pronto como reemplacé mi enfoque ingenuo de generación de números primos por un tamiz, mis tiempos de ejecución se redujeron a una cantidad razonable.

sebastiangeiger
fuente
1

Manteniéndolo muy simple ...

Encontrar los factores de X: comenzaría (n) en 2 y trabajaría hasta el número entero (piso, no redondo) de la raíz cuadrada de X. Si dividir X entre n produce Y e Y es un número entero, tanto n como Y son factores. Los valores más bajos de n producirán los valores más altos de Y.

Primalidad de Y: Nuevamente, repita (m) desde 2 hasta la raíz cuadrada de Y y vea si Y / m es un número entero. Si es así, Y no es primo. Regrese para encontrar otro factor.

Si m golpea la raíz de Y, tienes tu número primo. Deja de mirar. Y es la respuesta.

Si n golpea la raíz de X, no hay factores primos.

pdr
fuente
0

Como ya hay una solución completa, publicaré esta Haskell ...

--  Problem is to find the largest prime factor of 600851475143
module Factor (testval, bigfactor) where
  testval = 600851475143

  bf' :: Integer -> Integer -> Integer

  bf' f x | (f == x)         = f
          | ((mod x f) == 0) = bf' f (div x f)
          | True             = bf' (f+1) x

  bigfactor :: Integer -> Integer

  bigfactor x | (x <  1) = error "Parameter less than 1"
              | (x == 1) = 1
              | True     = bf' 2 x

Básicamente, no hay necesidad de probar la primalidad. Si divide los factores que encuentra (y se asegura de manejar los factores de repetición), entonces un factor no primo nunca puede ocurrir, porque un factor no primo es un producto de factores primos más pequeños.

Cargué y ejecuté esto con GHCi: es instantáneo, y ahora tengo un total de 4 (¡sí, cuatro!) Problemas de Euler resueltos.

Steve314
fuente
0

También estoy formando mi conocimiento de Python y también he comenzado a responder los problemas del Proyecto Euler en mi repositorio de github: https://github.com/rentes/Euler .

Para el problema 3, he programado una solución simple que se basa en las siguientes premisas:

1) dado un número entero positivo n, comienzo a dividirlo entre 2, 3, ..., m, y si encuentro que m es un factor primo, lo agrego a una lista. No estoy agregando a la lista múltiplos de factores primos ya descubiertos. Por ejemplo, 4 es un múltiplo de 2, por lo que 4 no se agrega a esta lista.

2) Luego multiplico cada primo en la lista para ver si es igual a n. Si es igual, hemos encontrado todos los factores primos de n. Si no, continúe dividiendo n entre el siguiente número m, hasta que la multiplicación de todos los factores primos sea igual a n o m llegue a n.

Consulte https://github.com/rentes/Euler/blob/master/problem3.py para obtener más detalles. He agregado comentarios que lo ayudarán a comprender lo que programé. Es una solución simple, y estoy seguro de que no es la solución más rápida, pero funciona y es lo suficientemente simple de entender.

Atentamente

Miguel Rentes
fuente