Factorizador entero tweetable más rápido

17

La tarea es encontrar un factor no trivial de un número compuesto.

Escriba código que encuentre un factor no trivial de un número compuesto lo más rápido posible, siempre que su código no tenga más de 140 bytes de longitud. La salida debería ser el factor que ha encontrado.

Su código puede tomar entrada y dar salida de cualquier manera que sea conveniente, incluyendo, por ejemplo, argumentos para una función.

Casos de prueba que enumeran todos los factores (solo necesita generar uno)

187: 11 17
1679: 23 73
14369648346682547857: 1500450271 9576890767
34747575467581863011: 3628273133 9576890767
52634041113150420921061348357: 2860486313 5463458053 3367900313
82312263010898855308580978867: 264575131106459 311111111111113
205255454905325730631914319249: 2860486313 71755440315342536873 
1233457775854251160763811229216063007: 1110111110111 1000000000063 1111111999999
1751952685614616185916001760791655006749: 36413321723440003717 48112959837082048697

No calificaré su respuesta en el siguiente caso de prueba difícil que puede ser de interés para la prueba:

513231721363284898797712130584280850383: 40206835204840513073 12764787846358441471

Puntuación

Su puntaje es el tiempo combinado para factorizar todos los casos de prueba anteriores con una penalización de 10 minutos por cada factorización fallida (todo redondeado al segundo más cercano). Su código también debería funcionar para otros enteros, eso no solo debería codificar estas respuestas.

Dejaré su código después de 10 minutos.

Si dos personas obtienen el mismo puntaje, la primera respuesta gana.

Restricciones

Su código no puede usar ninguna función incorporada o de biblioteca que realice la factorización de enteros. Puede suponer que la entrada toma menos de 256 bits. Todos los números de entrada serán compuestos.

¿Cómo voy a cronometrar?

Literalmente, ejecutaré time ./myprogen mi sistema Ubuntu para hacer el cronometraje, así que también proporcione un programa completo para que ejecute que incluya cualquier función que haya definido.

Una nota para idiomas compilados

El tiempo de compilación no debe tomar más de 1 minuto en mi máquina.

¿Es realmente posible?

Si ignora las restricciones de espacio, cada una de ellas puede factorizarse en menos de 2 segundos en mi computadora usando código Python puro + pypy.

Entonces, ¿qué es un algoritmo de factorización no trivial?

El algoritmo rho de Pollard es rápido y adecuado para jugar al golf. Por supuesto, también hay muchas otras formas de factorizar un número entero .

Aún más rápido es el tamiz cuadrático . Parece un desafío serio exprimir eso en 140 bytes.

Puntajes principales

  • SEJPM , 10 minutos de penalización por el último caso de prueba + 16 segundos en Haskell

fuente
Entonces, ¿se nos puede dar un número como 2 ** 1024?
Conor O'Brien
@ ConorO'Brien No se le dará nada con más dígitos que los casos de prueba.
Entonces, en términos de precisión, nada más que 256 bits.
Conor O'Brien
Para una entrada como 4, ¿la salida debería ser 2o 2, 2?
Sr. Xcoder
1
@AndersKaseorg Actualicé la pregunta siguiendo su sugerencia. Gracias.

Respuestas:

9

Haskell, 100 97 91 89 87 72 67 Bytes

¡Pruébelo en línea!

-3 bytes gracias a @flawr
-6 bytes gracias a @flawr nuevamente
-2 bytes gracias a @flawr una vez más
-2 bytes gracias a un conjunto optimizado de parámetros
-1 byte gracias a @flawrs otra vez
-14 bytes gracias al requisito a solo tener que generar un factor
-5 bytes gracias a @AndersKaseorg

f n|let s x=mod(x*x+7)n;a#b|d<-gcd(b-a)n,d>1=d|c<-s b=s a#s c=5#s 5

Esto funciona para los primeros 5 casos de prueba en un tiempo imperceptible.
Esto probablemente superará el tiempo en el caso de prueba más grande.

En general, esto generalmente devolverá un factor no trivial en el tiempo proporcional a la raíz cuadrada del factor más pequeño.
No funcionará en todas las entradas porque no varía el polinomio y la detección del caso excepcional es difícil de hacer en 140 bytes.
Tampoco generará la factorización completa, sino más bien un factor no trivial y la división de la entrada por este factor.
Tampoco ordenará los factores por tamaño.

El método utilizado es Factorización de Pollard-Rho con el valor inicial estándar de 2 (con el x^2+1polinomio estándar aplicado una vez) y el factor constante polinómico no estándar de 7 (porque 1no funcionó con 1679) para todas las evaluaciones posteriores.

Programa completo ( factor.hs):

import System.Environment(getArgs)

f n|let s x=mod(x*x+7)n;a#b|d<-gcd(b-a)n,d>1=d|c<-s b=s a#s c=5#s 5

main= do
      args <- getArgs
      print$f (read $ head args :: Integer)

Compilar como $ ghc factor.hs(necesita ghcinstalarse).
Corre como $ ./factor <number>.

Ejemplo de ejecución:

$ ./factor 187
11

Código sin golf:

f n=g 5 (s 5)
   where s x=mod(x*x+7)n
         g a b = if d>1 then d else g(s a)(s(s b))
               where d=gcd(b-a)n

Calcula el factor no trivial llamando gcon valores iniciales. El polinomio se aplica previamente en 2 aquí y se vuelve a aplicar en el resultado (5) para que la entrada a g(en una cláusula "where" ) siempre se pueda usar fácilmente para la prueba gcd. g(la versión con golf usa infijo #) luego intenta calcular un factor no trivial d(en la cláusula where en la versión sin golf, en línea en la versión con golf) como la diferencia entre las dos entradas g, si tiene éxito devuelve dicho factor de lo contrario, vuelve a intentarlo. Aquí puede generar ncomo salida si a==by, por lo tanto, devuelve solo un factor trivial, el enfoque adecuado para manejar esto sería variar los valores iniciales en este evento o cambiar el polinomio.

SEJPM
fuente
|1<2=s a#(s$s b)podría ser reemplazado con |c<-s b=s a#s cpienso :): (también ¿por qué no se contabiliza un TIO enlace?)
flawr
Actualicé la pregunta siguiendo las sugerencias de comentarios. Ahora solo necesita generar un factor y se garantiza que los números serán compuestos.
3
PS: ¿por qué estamos jugando al golf, esto no es ni siquiera código de golf
flawr
44
Ahora tiene 53 bytes para implementar un algoritmo de factorización aún más sofisticado :)
1
También se puede sacar abs , ya bque siempre es no negativo. (Quizás quisiste decir abs$b-a, pero gcdacepta argumentos negativos y siempre produce un resultado no negativo). ¡Eso reduce esto a menos de medio tuit!
Anders Kaseorg
6

Pari / GP , 137 bytes, ~ 5 segundos

Usando las operaciones de curva elíptica incorporadas de GP (y algunos ajustes de parámetros poco claros) :

ecm(n)=iferr(if(n%2==0,2,n%3==0,3,for(a=1,n,ellmul(ellinit(Mod([a,a^2-a-1],n)),[1,a],lcm([1..ceil(4^a^0.5)])))),e,gcd(n,lift(Vec(e)[3])))

ecmes una función que (debería) devolver un factor. Pruébalo en línea!

Prueba:

ecm(n)=iferr(if(n%2==0,2,n%3==0,3,for(a=1,n,ellmul(ellinit(Mod([a,a^2-a-1],n)),[1,a],lcm([1..ceil(4^a^0.5)])))),e,gcd(n,lift(Vec(e)[3])))

{
ns = [
  187,
  1679,
  14369648346682547857,
  34747575467581863011,
  52634041113150420921061348357,
  82312263010898855308580978867,
  205255454905325730631914319249,
  1233457775854251160763811229216063007,
  1751952685614616185916001760791655006749
  ]
}

test(n) = {
    d = ecm(n);
    if (!(1<d && d<n && n%d==0), error(d));
    print(n, ": ", d)
}

apply(test, ns)

quit

Sin golf:

ecm(n) = {
  iferr(if(n%2 == 0, 2,
           n%3 == 0, 3,
           for(a = 1, n,
               /* x^3 + A*x + B = y^2 */
               E = ellinit(Mod([a, a^2-a-1], n)); /* [A, B] */
               x0 = [1, a]; /* [x, y] */
               B = ceil(4^a^0.5); /* ~ exp(sqrt(log(p))), p ~= exp(a) */
               print("a=", a, ", B=", B);
               ellmul(E, x0, lcm([1..B]))
              )
          ),
         ERR, gcd(n, lift(Vec(ERR)[3] /* = Mod(d, n) */)),
         errname(ERR)=="e_INV")
}

Lamentablemente, manejar los factores 2 y 3 usa muchos bytes. Bytes que podrían haberse usado para agregar una etapa 2:

ecm(n)=iferr(for(a=1,n,Y=X=ellmul(E=ellinit(Mod([a,1],n)),[0,1],(B=ceil(4^a^0.5))!);for(z=0,9*B,Y=elladd(E,Y,X))),e,gcd(n,lift(Vec(e)[3])))
japh
fuente
1

Axioma, 137 bytes 9 minutos

p(n:PI):PI==(j:=1;a:=3;s:=n^.2;repeat(b:=j:=nextPrime(j);repeat(b<s=>(b:=b*j);break);a:=powmod(a,b,n);d:=gcd(a-1,n);d>1 or j>n=>break);d)

arriba de la función p () que implementaría algo p-1 para factorizar debajo de qué copiar en un archivo para probar en la función p ()

-- one has to copy this below text in a file name for example file.input
-- in one window where there is Axiom one could write 
-- )read C:\absolutepathwherethereisthatfile\file
-- and call the function test()
-- test()
-- the first character of all function and array must be afther a new line "\n"
)cl all
)time on
vA:=[187,1679,14369648346682547857,34747575467581863011,52634041113150420921061348357,82312263010898855308580978867,205255454905325730631914319249,1233457775854251160763811229216063007, 1751952685614616185916001760791655006749]

p(n:PI):PI==(j:=1;a:=3;s:=n^.2;repeat(b:=j:=nextPrime(j);repeat(b<s=>(b:=b*j);break);a:=powmod(a,b,n);d:=gcd(a-1,n);d>1 or j>n=>break);d)

-- this would try to factor n with p-1 Pollard method
pm1(n:PI):PI==
   j:=1;a:=3;s:=n^.2
   repeat
      b:=j:=nextPrime(j)
      repeat(b<s=>(b:=b*j);break)
      a:=powmod(a,b,n)
      d:=gcd(a-1,n);d>1 or j>n=>break
   d

test()==(for i in 1..#vA repeat output [vA.i, p(vA.i)])

resultados aquí:

(5) -> test()
   [187,11]
   [1679,73]
   [14369648346682547857,9576890767]
   [34747575467581863011,9576890767]
   [52634041113150420921061348357,2860486313]
   [82312263010898855308580978867,311111111111113]
   [205255454905325730631914319249,2860486313]
   [1233457775854251160763811229216063007,1111111999999]
   [1751952685614616185916001760791655006749,36413321723440003717]
                                                               Type: Void
                              Time: 496.78 (EV) + 53.05 (GC) = 549.83 sec
RosLuP
fuente
¿Podría explicar exactamente cómo ejecutar este código desde la línea de comandos en ubuntu, por favor? He instalado axiom e hice un archivo llamado foo.ax con su código no golfizado.
@Lembik 1) cambie el nombre de fop.ax en foo.input 2) ejecute Axiom en un terminal o xterm 3) escriba en ese terminal de Axiom el comando seguir ") lea C: absolutepath \ foo" 4) escriba en el terminal de Axiom la llamada para funcionar test (). Así es como se hace en Windows, la pista me parece abrir una sesión de Axiom y cargar el archivo con el comando ") leer"
RosLuP
@Lembik si hay un problema con los archivos, creo que también estaría bien: 1) ejecutar Axiom 2) escribir) hora en <return> en el programa Axiom 3) copiar y pegar y presionar volver en cada "copiar y pegar" en el programa Axiom: el array vA, la función p () y test () 4) en la prueba de escritura del programa Axiom () <return>
RosLuP
@Lembik, ¿qué tiempo lleva?
RosLuP
1

Axioma, 10 minutos + 31 segundos

A(a)==>a:=(a*a+7)rem n;z(n)==(p:=a:=b:=101;for i in 1..repeat(A(a);A(b);A(b);p:=mulmod(p,a-b,n);i rem 999<9=>(p:=gcd(p,n);p>1=>break));p)

z () es la función rho, una función de 137 bytes; ungolfed z () y llamarlo como rho (). Supondría que gcd (0, n) = n, por lo que el ciclo se detiene y regresa para fallar n.

)time on    
rho(n)==
  p:=a:=b:=101
  for i in 1..repeat
          A(a);A(b);A(b)
          p:=mulmod(p,a-b,n)
          i rem 999<9=>(p:=gcd(p,n);p>1=>break)
  p

va1:=[187,1679,14369648346682547857,34747575467581863011,52634041113150420921061348357,82312263010898855308580978867,205255454905325730631914319249,1233457775854251160763811229216063007, 1751952685614616185916001760791655006749]
p1()==(for i in 1..#va1-1 repeat output [va1.i,z(va1.i)]) 

los resultados (z () están bien para todos, pero el último número 1751952685614616185916001760791655006749 no se tienen en cuenta (10 minutos))

(6) -> p1()
   [187,17]
   [1679,23]
   [14369648346682547857,1500450271]
   [34747575467581863011,3628273133]
   [52634041113150420921061348357,2860486313]
   [82312263010898855308580978867,264575131106459]
   [205255454905325730631914319249,2860486313]
   [1233457775854251160763811229216063007,1111111999999]
                                                               Type: Void
                                 Time: 30.38 (EV) + 1.38 (GC) = 31.77 sec

(8) -> z(1679)
   (8)  23
                                                    Type: PositiveInteger
                                                              Time: 0 sec
RosLuP
fuente
0

Python 3 , 100 99 bytes, 45 40 39 segundos + 10 minutos de penalización

import math
def f(n):
 x=y=2;d=1
 while d<2:y=y*y+1;x,y=1+x*x%n,y*y%n+1;d=math.gcd(x-y,n)
 return d

Pruébalo en línea!

Utiliza Pollard-Rho con valor inicial 2 y polinomio x ^ 2 + 1.

techo
fuente
Podría usar pow(con el tercer argumento) para mejorar su velocidad de ejecución.
mbomb007