Section 12.6 A Taste of Modernity
ΒΆSubsection 12.6.1 The Pollard Rho algorithm
ΒΆHere is the essence of this random (or βMonte Carloβ) approach; it is highly recursive, like many good algorithms.Algorithm 12.6.1. Generic routine for βrandomβ factoring.
Follow these steps.
Pick some polynomial that will be easy to compute mod (n).
Plug in an essentially random seed value. (Often the seed is 2 or 3.)
Compute the polynomial's value at the seed.
If that has a non-trivial gcd with n, we have a factor. Otherwise, plug the new value back into the polynomial, and repeat (and hope it eventually succeeds).
xxxxxxxxxx
def PollardRhoFactor(n,kstop=50,seed=2):
d=1
a,b=seed,seed
k=1
def f(x):
return (x^2+1)%n
while (d==1 or d==n):
a = f(a)
b = f(f(b))
d=gcd(a-b,n)
k=k+1
if k>kstop:
print("Pollard Rho breaking off after %s rounds"%k)
break
if d>1:
print("Pollard Rho took %s rounds"%k)
print("The number it tried in the last round was %s, which shares factor %s"%(a-b,d))
print("And %s is a factor of %s since %s * %s=%s"%(d,n,d,n/d,d*(n/d)))
x0β‘2 (mod n)
x1β‘f(x0) (mod n)
x2β‘f(x1) (mod n)
xi+1β‘f(xi), all (mod n).
Example 12.6.2.
Let's try computing what we get for some specific numbers. Picking n=8051 as in [C.2.4, Example 3.25], we get results as in the following interact.
xxxxxxxxxx
var('x')
def _(seed=2,n=8051,poly=x^2+1,trials=(10,[2..50])):
f(x)=poly
for i in range(trials):
pretty_print(html("$x_{%s}=%s$"%(i,seed)))
seed = (ZZ(f(seed)) % ZZ(n))
pretty_print(html("$x_{%s}=%s$"%(i+1,seed)))
Notice that for n=8051, the term x4β‘x19 (mod n), so the sequence, while seeming fairly random, will not come up with every possible divisor. With seed 3, x13β‘x28 is the first repeat; if instead we change the modulus to 8053, there is no repeat through x50. So you can see the output will be hard to predict.
Algorithm 12.6.3. Pollard Rho factoring algorithm.
Follow these steps.
Pick some polynomial f(x) that will be easy to compute mod (n) (such as x^2+1\text{,} though other quadratics might be used).
Plug in an essentially random seed value x_0\text{.} (Often the seed is 2 or 3\text{.})
Compute the polynomial's value at the seed, f(x_0)=x_1\text{.}
Continue plugging in f(x_i)=x_{i+1}\text{,} modulo n\text{.}
-
For each k we check whether
\begin{equation*} 1<\gcd(x_{2k}-x_k,n)=d<n\text{.} \end{equation*}
Example 12.6.4.
Let's try this with n=9991\text{.} Keeping x^2+1 and seed 2\text{,} the numbers we would get for the first six rounds are
This gives differences as follows:
x_2-x_1=26-5=21
x_4-x_2=8735-26=8709
x_6-x_3=4654-677=3977
x_8-x_4=4973-8735=-3762
x_{10}-x_5=8153-8950=-797
These factor as follows:
xxxxxxxxxx
factor(21), factor(8709), factor(3977), factor(3672), factor(797)
That is an impressive list of eight different prime factors that could potentially be shared with 9991 in just five iterations. These differences have the following gcds with 9991\text{:}
xxxxxxxxxx
gcd(9991,21), gcd(9991,8709), gcd(9991,3977), gcd(9991,3672), gcd(9991,797)
Indeed the third one already caught a common divisor with 9991\text{.}
xxxxxxxxxx
PollardRhoFactor(8051)
Remark 12.6.5.
This method is called the Pollard rho method because (apparently) a very imaginative eye can interpret the x_i eventually repeating (mod d) (in the example, d=97) as a tail and then a loop, i.e. a Greek \rho\text{.} John Pollard actually has another method named after him, the p-1 method, which we will not explore; however, it is related to some of the more advanced methods we mentioned in the introduction to this section.
Example 12.6.6.
Sometimes the rho method doesn't come up with an answer quickly, or at all.
xxxxxxxxxx
PollardRhoFactor(991*997)
Here we took 50 rounds without success, using the seed 2\text{.} Because of the \rho repetition, it will never succeed. So what do you do then β bring out the really advanced methods? Not at all β just as with primality testing, you just change your starting point to try again!
xxxxxxxxxx
PollardRhoFactor(991*997,seed=3)
Subsection 12.6.2 More factorization
ΒΆIn general, there are other such probabilistic algorithms, and they are quite successful with factoring numbers which might have reasonably sized but not gigantic factors.Remark 12.6.7.
The big success of this algorithm was the 1980 factorization of F_8 (the eighth Fermat number) by Pollard and Richard Brent (see Brent's website). They used one of several variants of the method, due to Brent, to found the previously unknown prime factorβ3βIf you want to memorize this historic number, Brent provides the phrase βI am now entirely persuaded to employ the method, a handy trick, on gigantic composite numbersβ to help you remember it. 1238926361552897\text{.} Finding this factor of F_8 took, in total, 2 hours on a UNIVAC 1100/42 (which for the time period was very fast, indeed). Interestingly, the other (much larger) factor was not proven to be prime until significantly later.
xxxxxxxxxx
PollardRhoFactor(2^(2^8)+1,1000000) # one million rounds!
xxxxxxxxxx
PollardRhoFactor(2^(2^8)+1,1000000,seed=3)
xxxxxxxxxx
factor(2^(2^8)+1)
xxxxxxxxxx
def TrialDivFactor(n):
p = next_prime(1)
top = ceil(math.sqrt(n))
while p < top:
if mod(n,p)==0:
break
p=next_prime(p)
if n==1:
print("1 is not prime")
elif p==n:
print(n,"is prime")
elif mod(n,p)==0:
print(n,"factors as",p,"times",n/p)
else:
print(n,"is prime")
β
def FermatFactor(n,verbose=False):
if n%2==0:
raise TypeError("Input must be odd!")
s=ceil(math.sqrt(n))
top=(n+1)/2
while is_square(s^2-n)==0:
if verbose:
print(s,"squared minus",n,"is",s^2-n,"which is not a perfect square")
s=s+1
t=sqrt(s^2-n)
print("Fermat found that",s,"squared minus",t,"squared equals",n)
if s^2==n:
print("So",n,"was already a perfect square,",s,"times",s)
elif s<top:
print("So",s+t,"times",s-t,"equals",(s-t)*(s+t),"which is",n)
elif s==top:
print("So Fermat did not find a factor, which means",n,"is prime!")
xxxxxxxxxx
def _(n=991*997,method=['trial','Fermat','Pollard Rho']):
if method=='trial':
TrialDivFactor(n)
if method=='Fermat':
FermatFactor(n)
if method=='Pollard Rho':
PollardRhoFactor(n)
Sage note 12.6.8. Building interacts.
An interact is just a Sage/Python function, except with @interact
before it. There are many different input widgets you can use; this one demonstrates using a list and an input box which takes any input. See the interact documentation or Quickstart for many examples and more details.
Another interesting resource is Sage developer Paul Zimmermann's Integer Factoring Records. Finally, Wagstaff's The joy of factoring [C.4.12] has tons of awesome examples and procedures β far too many, really, as well as an excellent discussion of how to speed up trial division, etc.The Cunningham Project seeks to factor the numbers b^n \pm 1 for b=2, 3, 5, 6, 7, 10, 11, 12\text{,} up to high powers n\text{.}