Schnellster tweetbarer ganzzahliger Faktor

17

Die Aufgabe besteht darin, einen nicht trivialen Faktor einer zusammengesetzten Zahl zu finden.

Schreiben Sie Code, der einen nicht trivialen Faktor einer zusammengesetzten Zahl so schnell wie möglich findet, sofern Ihr Code nicht länger als 140 Byte ist. Die Ausgabe sollte nur der gefundene Faktor sein.

Ihr Code kann Eingaben und Ausgaben auf jede beliebige Weise annehmen, einschließlich beispielsweise als Argumente für eine Funktion.

Testfälle, in denen alle Faktoren aufgelistet sind (Sie müssen nur einen ausgeben)

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

Ich werde Ihre Antwort nicht für den folgenden schwierigen Testfall bewerten, der für das Testen von Interesse sein kann:

513231721363284898797712130584280850383: 40206835204840513073 12764787846358441471

Ergebnis

Ihre Punktzahl ist die kombinierte Zeit, um alle oben genannten Testfälle mit einer Strafe von 10 Minuten für jede fehlgeschlagene Faktorisierung zu faktorisieren (alle auf die nächste Sekunde gerundet). Ihr Code sollte auch für andere Ganzzahlen funktionieren, das heißt, Sie sollten diese Antworten nicht nur hartcodieren.

Ich werde Ihren Code nach 10 Minuten stoppen.

Erhalten zwei Personen die gleiche Punktzahl, gewinnt die erste Antwort.

Beschränkungen

Ihr Code kann keine eingebauten oder Bibliotheksfunktionen verwenden, die eine Ganzzahlfaktorisierung durchführen. Sie können davon ausgehen, dass die Eingabe weniger als 256 Bit dauert. Alle Eingabenummern werden zusammengesetzt.

Wie werde ich mal?

Ich werde buchstäblich time ./myprogauf meinem Ubuntu-System laufen , um das Timing zu erledigen. Geben Sie daher bitte auch ein vollständiges Programm an, das alle von Ihnen definierten Funktionen enthält.

Ein Hinweis für kompilierte Sprachen

Die Kompilierungszeit auf meinem Computer darf nicht länger als 1 Minute dauern.

Ist das überhaupt möglich?

Wenn Sie die Platzbeschränkungen ignorieren, kann jeder in weniger als 2 Sekunden auf meinem Computer mit reinem Python-Code + pypy berücksichtigt werden.

Was ist ein nicht-trivialer Faktorisierungsalgorithmus?

Pollards Rho-Algorithmus ist schnell und eignet sich zum Golfen. Natürlich gibt es auch viele andere Möglichkeiten, eine ganze Zahl zu faktorisieren.

Noch schneller ist das quadratische Sieb . Es sieht nach einer ernsthaften Herausforderung aus, dies in 140 Bytes zusammenzufassen.

Führende Ergebnisse

  • SEJPM , 10 Minuten Strafe für den letzten Testfall + 16 Sekunden in Haskell

quelle
So können wir eine Zahl wie gegeben werden 2 ** 1024?
Conor O'Brien
@ ConorO'Brien Du bekommst nichts mit mehr Ziffern als die Testfälle.
Also, in Bezug auf die Präzision, nicht mehr als 256 Bit.
Conor O'Brien
Sollte für eine Eingabe wie 4 die Ausgabe sein 2oder 2, 2?
Mr. Xcoder
1
@AndersKaseorg Ich habe die Frage aufgrund Ihres Vorschlags aktualisiert. Vielen Dank.

Antworten:

9

Haskell, 100 97 91 89 87 72 67 Bytes

Probieren Sie es online!

-3 Bytes dank @flawr
-6 Bytes dank @flawr nochmal
-2 Bytes dank @flawr nochmal
-2 Bytes dank optimiertem Parametersatz
-1 Bytes dank @flawrs nochmal
-14 Bytes dank Anforderung Dank @AndersKaseorg müssen Sie nur einen Faktor
-5 Byte ausgeben

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

Dies funktioniert für die ersten 5 Testfälle in nicht wahrnehmbarer Zeit.
Bei dem größten Testfall tritt wahrscheinlich eine Zeitüberschreitung auf.

Im Allgemeinen gibt dies normalerweise einen nicht-trivialen Faktor in der Zeit zurück, der proportional zur Quadratwurzel des kleinsten Faktors ist.
Es funktioniert nicht bei jeder Eingabe, da das Polynom nicht variiert und die Erkennung des Ausnahmefalls in 140 Bytes schwierig ist.
Es wird auch nicht die vollständige Faktorisierung ausgegeben, sondern ein nicht trivialer Faktor und die Division der Eingabe durch diesen Faktor.
Die Faktoren werden auch nicht nach Größe sortiert.

Die verwendete Methode ist Pollard-Rho-Factoring mit dem Standard-Startwert 2 (mit dem x^2+1einmal angewendeten Standard- Polynom) und dem Nicht-Standard-Polynom-Konstantenfaktor 7 (weil 1mit 1679 nicht funktioniert) für alle weiteren Auswertungen.

Vollständiges Programm ( 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)

Kompilieren als $ ghc factor.hs(muss ghcinstalliert sein).
Führen Sie so $ ./factor <number>.

Beispiellauf:

$ ./factor 187
11

Ungolfed-Code:

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

Berechnet den nicht trivialen Faktor durch Aufrufen gmit Anfangswerten. Das Polynom wird hier auf 2 vor- und auf das Ergebnis (5) erneut angewendet, so dass die Eingabe in g(in einer "where" -Klausel ) immer leicht für den gcd-Test verwendet werden kann. g(golfed version using infix #) versucht dann, einen nicht-trivialen Faktor d(in der where-Klausel in der nicht-golfed-Version, in der golfed-Klausel) als Differenz zwischen den beiden Eingaben zu berechnen g, falls dies erfolgreich ist, und gibt diesen Faktor zurück , sonst erneut versuchen. Hier kann es nals Ausgabe generiert werden, wenn a==bund daher nur ein trivialer Faktor zurückgegeben wird. Die richtige Vorgehensweise wäre, entweder die Startwerte bei diesem Ereignis zu variieren oder das Polynom zu ändern.

SEJPM
quelle
|1<2=s a#(s$s b)könnte durch ersetzt werden, |c<-s b=s a#s cdenke ich :) (auch: warum
postest
Ich habe die Frage gemäß den Kommentaren aktualisiert. Jetzt müssen Sie nur noch einen Faktor ausgeben und die Zahlen sind garantiert zusammengesetzt.
3
PS: warum
golfen
4
Sie haben jetzt 53 Bytes, in denen Sie einen noch
1
Auch das kann man rausnehmen abs , da bdas immer nicht negativ ist. (Vielleicht haben Sie das gemeint abs$b-a, gcdakzeptieren aber negative Argumente und führen immer zu einem nicht negativen Ergebnis.) Dies führt zu weniger als einem halben Tweet!
Anders Kaseorg
5

Pari / GP , 137 Bytes, ~ 5 Sekunden

Verwenden der integrierten elliptischen Kurvenoperationen von GP (und einiger hinterhältiger Parameterabstimmungen) :

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])))

ecmist eine Funktion, die einen Faktor zurückgeben soll. Probieren Sie es online!

Prüfung:

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

Ungolfed:

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")
}

Die Behandlung der Faktoren 2 und 3 benötigt leider viele Bytes. Bytes, die zum Hinzufügen einer Stufe 2 hätten verwendet werden können:

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
quelle
1

Axiom, 137 Bytes, 9 Minuten

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)

über der Funktion p (), die p-1-Algorithmus zum Faktorisieren implementieren würde, unter dem, was in eine Datei zum Testen auf p () -Funktion kopiert werden soll

-- 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)])

Ergebnisse hier:

(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
quelle
Könnten Sie bitte genau beschreiben, wie Sie diesen Code über die Befehlszeile in Ubuntu ausführen? Ich habe Axiom installiert und eine Datei mit dem Namen foo.ax erstellt, in der sich Ihr nicht-golfener Code befindet.
@Lembik 1) benenne fop.ax in foo.input um 2) führe Axiom in einem Terminal oder xterm aus 3) schreibe in dieses Axiom-Terminal den Befehl follow ") read C: absolutepath \ foo" 4) schreibe in das Terminal von Axiom den Aufruf zum Funktionstest (). Dies ist, wie in Windows zu tun, der Hinweis, es scheint mir, öffnen Sie eine Axiom-Sitzung und laden Sie die Datei mit ") read" -Befehl
RosLuP
@Lembik Wenn es ein Problem mit Dateien gibt, denke ich, wäre es auch in Ordnung: 1) Axiom ausführen 2) Bei <return> in Axiom-Programm schreiben 3) Kopieren Einfügen und bei jedem "Kopieren Einfügen" in Axiom-Programm die Eingabetaste drücken: Die Array VA, die Funktion p () und test () 4) im Axiom-Programm schreiben test () <return>
RosLuP
@Lembik also wie lange dauert es?
RosLuP
1

Axiom, 10 Minuten + 31 Sekunden

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 () ist die Funktion rho, eine 137-Byte-Funktion; ungolfed z () und nenne es rho (). Es wird angenommen, dass gcd (0, n) = n ist, sodass die Schleife stoppt und bei Fehler n zurückkehrt.

)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)]) 

Ergebnisse (z () ist in Ordnung für alle bis auf die letzte Nummer 1751952685614616185916001760791655006749 bleiben unberücksichtigt (10 Minuten))

(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
quelle
0

Python 3 , 100 99 Bytes, 45 40 39 Sekunden + 10 Minuten Strafe

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

Probieren Sie es online!

Verwendet Pollard-Rho mit dem Anfangswert 2 und dem Polynom x ^ 2 + 1.

Ceilingcat
quelle
Sie könnten pow(mit dem 3. Argument) verwenden, um Ihre Ausführungsgeschwindigkeit zu verbessern.
mbomb007