Fastest tweetable integer factorizer

17

4

The task is to find a non-trivial factor of a composite number.

Write code that finds a non-trivial factor of a composite number as quickly as possible subject to your code being no more than 140 bytes long. The output should just be the factor you have found.

Your code can take input and give output in any way that is convenient including for example as arguments to a function.

Test cases which list all the factors (you only need to output one)

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

I won't score your answer on the following difficult test case which may be of interest for testing:

513231721363284898797712130584280850383: 40206835204840513073 12764787846358441471

Score

Your score is the combined time to factor all the test cases above with a penalty of 10 minutes for each failed factorization (all rounded to the nearest second). Your code should work for other integers too, that is shouldn't just hardcode these answers.

I will stop your code after 10 minutes.

If two people get the same score then the first answer wins.

Restrictions

Your code can't use any builtin or library function that performs integer factorization. You can assume the input takes less than 256 bits. All input numbers will be composite.

How will I time?

I will literally run time ./myprog on my Ubuntu system to do the timing so please also supply a complete program for me to run that includes any function you have defined.

A note for compiled languages

Compilation time must take no more than 1 minute on my machine.

Is it actually possible?

If you ignore the space constraints, then each one can be factored in less than 2 seconds on my computer using pure Python code + pypy.

So what is a non-trivial factoring algorithm?

Pollard's rho algorithm is fast and suitable for golfing. Of course there are plenty of other ways to factor an integer as well.

Even faster is the Quadratic sieve. It looks like a serious challenge to squeeze that into 140 bytes.

Leading scores

  • SEJPM, 10 minutes penalty for the last test case + 16 seconds in Haskell

user9206

Posted 2017-07-08T19:10:29.513

Reputation:

So, we may be given a number like 2 ** 1024? – Conor O'Brien – 2017-07-08T19:23:32.707

@ConorO'Brien You won't be given anything with more digits than the test cases. – None – 2017-07-08T19:35:44.297

So, in terms of precision, nothing more than 256 bits. – Conor O'Brien – 2017-07-08T19:37:24.470

For an input like 4, should the output be 2 or 2, 2? – Mr. Xcoder – 2017-07-08T20:37:19.820

Are you sure that the last test case is possible in 10 minutes? – LegionMammal978 – 2017-07-08T22:03:24.640

@LegionMammal978 msieve (a dedicated C/C++ factoring tool) can solve the last test case in about 2 seconds. sage needs about the same time with a simple call to factor. Whether it is feasible to put the algorithms used in these tools into 140 chars is a different question... – SEJPM – 2017-07-08T22:05:34.927

The only answer so far breaks the specification by merely finding a factor of a composite number, and not a full prime factorization. However, perhaps that would a better challenge, so that one doesn’t need to squeeze both factoring and primality testing into the same tweet? (By themselves, I think the fastest factoring algorithms will tend to run slowly or loop infinitely trying to factor the remaining primes after all the proper factors have been found.) – Anders Kaseorg – 2017-07-08T22:52:38.523

1@AndersKaseorg I updated the question following your suggestion. Thanks. – None – 2017-07-09T06:05:08.140

Why the downvote? Please add an explanation. I may even be able to fix what you don't like. – None – 2017-07-09T11:02:35.463

@Lembik I suppose in the Haskell solution the 1751952685614616185916001760791655006749 would be the number not factored in 10 minutes; so I suppose you say that for factoring 82312263010898855308580978867 Haskell code take 16 seconds? How is possible than in Tio that code in 1 minute (I suppose) not factored it? – RosLuP – 2017-09-28T19:12:46.343

Answers

9

Haskell, 100 97 91 89 87 72 67 Bytes

Try It Online!

-3 bytes thanks to @flawr
-6 bytes thanks to @flawr again
-2 bytes thanks to @flawr yet again
-2 bytes thanks to an optimized set of parameters
-1 byte thanks to @flawrs yet another time
-14 bytes thanks to requirement to only having to output one factor
-5 bytes thanks to @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

This works for the first 5 test cases in unnoticable time.
This will probably time-out on the largest test case.

In general this will usually return one non-trivial factor in time proportional to the square root of the smallest factor.
It will not work on every input because it doesn't vary the polynomial and detection of the exceptional case is hard to do in 140 bytes.
It will also not output the full factorization, but rather a non-trivial factor and the division of the input by this factor.
It will also not sort the factors by size.

The method used is Pollard-Rho-Factoring with the standard starting value of 2 (with the standard x^2+1 polynomial applied once) and the non-standard polynomial constant factor of 7 (because 1 didn't work with 1679) for all further evaluations.

Full program (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)

Compile as $ ghc factor.hs (needs ghc installed).
Run as $ ./factor <number>.

Example run:

$ ./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

Computes the non-trivial factor by calling g with initial values. The polynomial is pre-applied on 2 here and re-applied on the result (5) so that the input to g (in a "where" clause) can always be readily used for the gcd-test. g (golfed version uses infix #) then tries to compute a non-trivial factor d (in the where clause in the un-golfed version, in-line in the golfed one) as the difference between the two inputs to g, if it succeeds returns said factor, else retries. Here it may generate n as the output if a==b and thus returns only a trivial factor, the proper approach to handle this would be to either vary the starting values upon this event or change the polynomial.

SEJPM

Posted 2017-07-08T19:10:29.513

Reputation: 3 203

|1<2=s a#(s$s b) could be replaced with |c<-s b=s a#s c I think :) (also: why don't you post a TIO link?) – flawr – 2017-07-08T23:14:11.570

I updated the question following the comment suggestions. Now you only need to output one factor and the numbers are guaranteed to be composite. – None – 2017-07-09T05:47:19.440

3PS: why are we golfing, this isn't even [tag:code-golf] – flawr – 2017-07-09T09:05:17.700

4You now have 53 bytes in which to implement an even more sophisticated factoring algorithm :) – None – 2017-07-09T11:55:05.857

Using |let rather than where for local declarations saves a byte.

– Anders Kaseorg – 2017-07-10T03:45:42.920

1Also you can take out abs, since b is always nonnegative. (Perhaps you meant abs$b-a, but gcd accepts negative arguments and always produces a nonnegative result.) That brings this down to less than half a tweet! – Anders Kaseorg – 2017-07-10T05:33:48.600

"It will also not work on the last test case because it is not semi-prime" but the last case IS semi-prime! And you only need to output one factor! – NieDzejkob – 2018-08-10T23:13:21.670

@NieDzejkob in the first version of the question the last test case had 4 factors (three large ones and 2), which is what I was referring to with that line, which is now removed (you may flag this for "no longer needed" once read). – SEJPM – 2018-08-11T09:08:48.653

6

Pari/GP, 137 bytes, ~5 seconds

Using GP's built-in elliptic curve operations (and some underhanded parameter tuning):

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

ecm is a function that (ought to) return a factor. Try it online!

Test:

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

Sadly, handling the factors 2 and 3 uses many bytes. Bytes that could have been used to add a stage 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

Posted 2017-07-08T19:10:29.513

Reputation: 751

1

Axiom, 137 bytes 9 minutes

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)

above the function p() that would implement p-1 algo for factoring below what to copy in a file for test on p() function

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

results here:

(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

Posted 2017-07-08T19:10:29.513

Reputation: 3 036

Could you spell out exactly how to run this code from the command line in ubuntu please? I have installed axiom and made a file called foo.ax with your non-golfed code in it. – None – 2017-08-07T17:34:04.153

@Lembik 1)rename fop.ax in foo.input 2) run Axiom in one terminal or xterm 3) write in that Axiom terminal the follow command ")read C:absolutepath\foo" 4) write in the terminal of Axiom the call to function test(). This is how to do in Windows, the clue it seems to me open one Axiom session and load the file with ")read" command – RosLuP – 2017-08-07T20:44:44.603

@Lembik if there are problem with files I think it would be ok too: 1) run Axiom 2) write )time on <return> in Axiom program 3) copy paste and press return in each "copy paste" in Axiom program: the array vA, the function p() and test() 4) in the Axiom program write test()<return> – RosLuP – 2017-08-09T04:31:26.590

@Lembik so what time it takes? – RosLuP – 2017-08-29T07:21:31.137

1

Axiom, 10 minutes + 31 seconds

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() is the function rho, one 137 bytes function; ungolfed z() and call it as rho(). It would suppose that gcd(0,n)=n so the loop stop and return for fail 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)]) 

results (z() is ok to all but the last number 1751952685614616185916001760791655006749 remain not factored (10 minutes) )

(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

Posted 2017-07-08T19:10:29.513

Reputation: 3 036

0

Python 3, 100 99 bytes, 45 40 39 seconds + 10 minute penalty

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

Try it online!

Uses Pollard-Rho with initial value 2 and polynomial x^2+1.

ceilingcat

Posted 2017-07-08T19:10:29.513

Reputation: 5 503

You could use pow (with the 3rd argument) to improve your execution speed.

– mbomb007 – 2018-05-30T14:41:45.607