Séance 8 : factorisation d’entiers – crible quadratique#
Exercice#
Dans cet exercice, on propose d’implémenter (de façon naïve et non-optimisée) l’algorithme de crible quadratique qui trouve un diviseur propre d’un entier \(N\).
Question 1. Écrire une fonction calcule_base_facteurs(N, B) qui calcule une base de facteurs plus petits que B dans le but de factoriser un entier N.
def calcule_base_facteurs(N, B):
L = [ 2 ]
p = 3
while p <= B:
if jacobi_symbol(N, p) == 1:
L.append(p)
p = next_prime(p)
return L
print(calcule_base_facteurs(N=369713, B=21))
[2, 7, 11, 19]
Question 2. Écrire une fonction de quad_sieve(FB, N, A) qui prend en entrée une base de facteurs FB, un entier N à factoriser et une borne A, et qui effectue l’étape d’effritement (crible).
def quad_sieve(FB, N, A):
S = ceil(sqrt(N))
Q = lambda x: x**2 + 2*S*x + S**2 - N
T = [ Q(x) for x in range(A) ]
for p in FB:
a = mod(N, p)
if a.is_square():
roots = a.sqrt(all=True)
roots = [Integer(r-S) for r in roots ]
roots = [r for r in roots if r < A ]
e = 1
while roots != []:
for x in roots:
while x < A:
T[x] = T[x]//p
x = x + p**e
e += 1
a = mod(N, p**e)
roots = a.sqrt(all=True)
roots = [Integer(r-S) for r in roots ]
roots = [r for r in roots if r < A ]
return [ [x,Q(x)] for x in range(A) if T[x] == 1 ]
N = 369713
FB = calcule_base_facteurs(N=369713, B=21)
A = ceil(sqrt(N))//3
L = quad_sieve(FB, N, A)
print(L)
[[6, 8512], [8, 10976], [24, 30976], [106, 141512], [120, 161728]]
Question 3. Écrire une fonction de build_matrix(FB, sieve) qui prend en entrée une base de facteurs FB et une liste d’entiers effrités sieve, et qui construit la matrice associée.
def build_matrix(FB, res_sieve):
n = len(FB)
m = len(res_sieve)
M = zero_matrix(ZZ, m, n)
for i in range(m):
r = res_sieve[i][1]
for j in range(n):
p = FB[j]
while (r != 0) and ((r % p) == 0):
M[i,j] = M[i,j] + 1
r = r//p
return M
M = build_matrix(FB, L)
print(M)
[6 1 0 1]
[5 3 0 0]
[8 0 2 0]
[3 2 0 2]
[6 1 0 2]
Question 4. Assembler les fonctions pour obtenir une fonction qui, étant donné un entier N en entrée, retourne un diviseur de N grâce à l’algorithme du crible quadratrique.
def quadratic_sieve(N, verbose=False):
# Initialisation des constantes
sqN = ceil(sqrt(N))
Q = lambda x: x**2 + 2*sqN*x + sqN**2 - N
A = sqN//3 # A = sqN
B = floor(2**(0.5*sqrt(log(N, 2) * log(log(N, 2), 2))))
B_large_enough = False
# Collecte de relations (en avoir + que de facteurs)
# et solution du système
while not(B_large_enough):
FB = calcule_base_facteurs(N, B)
s = len(FB)
sieve = quad_sieve(FB, N, A)
M = build_matrix(FB, sieve)
M2 = matrix(GF(2), M)
B_large_enough = (M2.kernel().dimension() > 0)
if not(B_large_enough) and verbose:
print("on augmente B")
B *= 2
# Reconstruction d'un diviseur
factor_found = False
while not(factor_found):
u = M2.left_kernel().random_element()
a = 1
b = 1
vec = vector(ZZ, [0 for j in range(s)])
for i in range(len(u)):
if u[i] == 1:
a = (a * (sieve[i][0] + sqN)) % N
vec += M[i]
vec = vec/2
for j in range(s):
b = (b * FB[j]**vec[j]) % N
p, q = gcd(a-b, N), gcd(a+b, N)
factor_found = (p not in [1, N]) or (q not in [1, N])
if not(factor_found) and verbose:
print("facteur non trouvé, on réessaie")
return p, q
Question 5. Testez votre fonction avec \(N =\) par exemple.
# N1 = 73217
N2 = 369713
# N3 = 7029871
# print(quadratic_sieve(N1))
print(quadratic_sieve(N2))
# print(quadratic_sieve(N3))
(457, 809)