L3 Informatique MPI4 – TD 8 : Nombres premiers
Premiers pas.
Écrire une fonction primes(n) retournant la liste des nombres premiers inférieurs à . On pourra utiliser le crible d'Eratosthène (http://fr.wikipedia.org /wiki/Crible_d'%C3%89ratosth%C3%A8ne).
Pour trouver les nombres premiers entre 2 et , on part d'une liste pp contenant seulement 2, et d'une liste mm contenant tous les nombres entre 2 et qui ne sont pas multiple de 2. mm[0] est donc 3, qui est forcément premier puisque pas multiple des nombres premiers qui lui sont inférieurs. On fait donc passer 3 à la fin de pp et on élimine de mm tous les multiples de 3. On recommence, 5 passe dans pp et les multiples de 5 sont éliminés. A chaque étape, mm[0] est un nombre premier , on l'ajoute à pp et on élimine ses multiples de mm, jusqu'à ce que mm soit vide.
Calculer primes(10000) et comparer avec ce qu'on obtient en filtrant range(10000) avec 4 tests de Miller-Rabin.
In [1]: def primes(n):
if n<2: return []
pp = [2]; mm = [m for m in range(3,n) if m%2]
while mm:
p = mm[0]
pp.append(p)
mm = [m for m in mm[1:] if m%p]
return pp
In [2]: print (primes(100))
In [3]: # Miller-Rabin
from random import randrange def test_base(a,n):
m = n-1; k = 0 while not m%2:
k+=1; m//=2 b = pow(a,m,n) if b==1: return True for i in range(k):
x = b
b = pow(b,2,n) if b==1:
if x != n-1: return False else: return True return False
def miller_rabin(n, tests=4):
if n in [2,3]: return True if not n%2: return False for i in range(tests):
a = randrange(2,n-1)
if not test_base(a,n): return False return True
pp = primes(10000)
mr = [p for p in range(2,10000) if miller_rabin(p)]
In [4]: [q for q in mr if q not in pp]
In [5]: miller_rabin(1729, tests=4)
n
n n
p
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
Out[4]: []
Out[5]: False
mpi4_td8_2020-sol_py3 http://www-igm.univ-mlv.fr/~jyt/L3_MPI4/mpi4_...
1 sur 4 10/12/2020 10:56
Factorisation
Force brute
Pour factoriser , précalculer une liste des premiers nombres premiers, tester s'ils divisent , et répéter récursivement sur le quotient. Si est le dernier de la liste et , c'est fini, sinon il faut continuer : tester les facteurs jusqu'à ce que . On pourra écrire une fonction intermédiaire trial_division(n) qui retourne le premier diviseur rencontré. Est-il nécessairement premier ?
Force brute améliorée
On se contente de la liste [2,3,5] et on repart de 7. On a , et si le facteur testé n'est pas congru à 1 ou à un nombre premier modulo 30, alors et il ne peut pas être premier. On incrémentera donc de 4,2,4,2,4,6,2,6 (périodicité 8) de manière à ce que soit successivement 11,13,17,19,23,29,1,7. Comparer les temps d'exécution des deux méthodes.
In [7]: def trial_division(n):
if n == 1: return 1 if n%2 == 0: return 2 if n%3 == 0: return 3 if n%5 == 0: return 5 if n<30: return n p = 7
while p*p <= n:
if n%p == 0: return p p +=2
return n
In [8]: def F(n):
return 2**(2**n)+1 print (F(6))
print (trial_division(F(6)))
In [9]: def trial_division(n, bound=None):
if n == 1: return 1 for p in [2, 3, 5]:
if n%p == 0: return p if bound == None: bound = n dif = [6, 4, 2, 4, 2, 4, 6, 2]
m = 7; i = 1
while m <= bound and m*m <= n:
if n%m == 0:
return m m += dif[i%8]
i += 1 return n def factor(n):
if n in [-1, 0, 1]: return []
if n < 0: n = -n F = []
while n != 1:
p = trial_division(n) e = 1
n //= p while n%p == 0:
e += 1; n //= p F.append((p,e)) F.sort()
return F
In [10]: factor(F(5))
In [11]: factor(F(6))
n M n p
n≤p2 fk=p+ 2k n≤fk2
2 × 3 × 5 = 30 f
f∧ 30 ≠ 1 f f mod 30
18446744073709551617 274177
Out[10]: [(641, 1), (6700417, 1)]
Out[11]: [(274177, 1), (67280421310721, 1)]
mpi4_td8_2020-sol_py3 http://www-igm.univ-mlv.fr/~jyt/L3_MPI4/mpi4_...
2 sur 4 10/12/2020 10:56
Test de Lucas
C'est la réciproque du théorème de Fermat. On rappelle que ce dernier est un cas particulier du théorème de Lagrange : dans un groupe fini, l'ordre de tout élément est un diviseur de l'ordre du groupe. Ainsi, si est premier, est un corps, et son groupe multiplicatif est d'ordre . Mais si n'est pas premier, l'ordre de ce groupe est strictement inférieur à . Donc, si on peut trouver un élément a d'ordre exactement n−1, n sera nécessairement premier.
Pour appliquer cette idée, on factorise pour avoir la liste de ses diviseurs premiers. On calcule ensuite pour diverses valeurs de telles que , les pour tous les . Si pour un certain a aucun n'est égal à 1, on a la preuve que n est premier.
In [12]: def lucas(n, tests=4):
bases = [randrange(2,n-1) for i in range(tests)]
for a in bases:
if pow(a,n-1,n) != 1: return False # n n'est pas permier Q = [x[0] for x in factor(n-1)]
for a in bases:
if all([(pow(a,(n-1)//q,n) != 1) for q in Q]):
return True # n est premier
return 'FAIL' # On ne peut pas conclure, il faut d'autres essais
In [13]: n = 2**107-1 lucas(n)
In [14]: lucas(F(4))
Test de Lucas-Lehmer.
Il permet de savoir si un nombre de Mersenne (https://fr.wikipedia.org/wiki/Nombre_de_Mersenne_premier) est premier. Voir là (https://en.wikipedia.org /wiki/Lucas–Lehmer_primality_test) pour les détails.
Pour prouver que est premier, on suppose qu'il possède un diviseur premier avec . Le groupe des éléments inversibles de est d'ordre au plus . Si on peut trouver un élément d'ordre supérieur à celui de ce groupe, cela prouvera qu'un tel n'existe pas.
Soient et . On a , et la suite vérifie et . Si on suppose que , on a
donc
donc , d'où
et
Ainsi, est d'ordre dans . Comme l'ordre de est , c'est impossible.
Donc, si , alors est premier.
Le plus grand nombre premier connu (https://primes.utm.edu/largest.html#largest) est en général un nombre de Mersenne.
In [15]: def lehmer(p, verbose=False):
assert miller_rabin(p) M = pow(2,p)-1 s = 4
for i in range(p-2):
u,s = s, (s*s-2)%M if s%M == 0:
if verbose: print ('M(%d) = (%d) est premier' % (p,M)) return True
else:
if verbose: print ('M(%d) = (%d) est composé' % (p,M)) return False
In [16]: n=2**107-1;n
In [17]: lucas(n,tests=8)
n Z/nZ n− 1 n
n− 1
n− 1 Q a
≡ 1 mod n
an−1 a(n−1)/q mod n q∈Q
= − 1
Mp 2p q q2≤Mp G
[ ]
Zq√3– q2− 1 ≤2p− 2 a q
z= 2 + 3–√ z¯= 2 −√3– zz¯= 1 sn=z( )2n +z¯( )2n s0= 4 sn=s2n−1− 2 Mp|sp−2
+ =k
z(2p−2) z¯(2p−2) Mp
=k − 1
z(2p−1) Mpz(2p−2)
≡ −1 mod q z(2p−1)
≡ 1 mod q z( )2p
z 2p G G <2p− 2
|
Mpsp−2 Mp Out[13]: True
Out[14]: True
Out[16]: 162259276829213363391578010288127
Out[17]: True
mpi4_td8_2020-sol_py3 http://www-igm.univ-mlv.fr/~jyt/L3_MPI4/mpi4_...
3 sur 4 10/12/2020 10:56
In [18]: lehmer(107)
In [ ]: lehmer(107,verbose=True)
In [ ]: mm = [p for p in pp if lehmer(p)]
In [ ]: print (mm)
In [ ]:
In [ ]:
Out[18]: True
M(107) = (162259276829213363391578010288127) est premier Out[ ]: True