Add much faster integer factorization (#403 related to #199)

This commit is contained in:
Dan 2017-11-03 12:59:17 +01:00 committed by Lonami
parent 9a12738f0e
commit 0bfd8ff032

View File

@ -1,71 +1,45 @@
from random import randint from random import randint
try:
import sympy.ntheory
except ImportError:
sympy = None
class Factorization: class Factorization:
@staticmethod @classmethod
def find_small_multiplier_lopatin(what): def factorize(cls, pq):
"""Finds the small multiplier by using Lopatin's method""" if pq % 2 == 0:
g = 0 return 2, pq // 2
for i in range(3):
q = (randint(0, 127) & 15) + 17
x = randint(0, 1000000000) + 1
y = x
lim = 1 << (i + 18)
for j in range(1, lim):
a, b, c = x, x, q
while b != 0:
if (b & 1) != 0:
c += a
if c >= what:
c -= what
a += a
if a >= what:
a -= what
b >>= 1
x = c y, c, m = randint(1, pq - 1), randint(1, pq - 1), randint(1, pq - 1)
z = y - x if x < y else x - y g = r = q = 1
g = Factorization.gcd(z, what) x = ys = 0
if g != 1:
while g == 1:
x = y
for i in range(r):
y = (pow(y, 2, pq) + c) % pq
k = 0
while k < r and g == 1:
ys = y
for i in range(min(m, r - k)):
y = (pow(y, 2, pq) + c) % pq
q = q * (abs(x - y)) % pq
g = cls.gcd(q, pq)
k += m
r *= 2
if g == pq:
while True:
ys = (pow(ys, 2, pq) + c) % pq
g = cls.gcd(abs(x - ys), pq)
if g > 1:
break break
if (j & (j - 1)) == 0: return g, pq // g
y = x
if g > 1:
break
p = what // g
return min(p, g)
@staticmethod @staticmethod
def gcd(a, b): def gcd(a, b):
"""Calculates the greatest common divisor""" while b:
while a != 0 and b != 0: a, b = b, a % b
while b & 1 == 0:
b >>= 1
while a & 1 == 0: return a
a >>= 1
if a > b:
a -= b
else:
b -= a
return a if b == 0 else b
@staticmethod
def factorize(pq):
"""Factorizes the given number and returns both
the divisor and the number divided by the divisor
"""
if sympy:
return tuple(sympy.ntheory.factorint(pq).keys())
else:
divisor = Factorization.find_small_multiplier_lopatin(pq)
return divisor, pq // divisor