TP 8 : algorithmes et arithmétique 1 L`algorithme d

publicité
T.P. 8 : algorithmes et arithmétique
1
L’algorithme d’Euclide étendu
Implémenter en Python, l’algorithme d’Euclide étendu vu au cours § 1.4. Par commodité, le
voici explicité sur un exemple appliqué à a = 19 et b = 15.
k
0
1
2
3
4
5
uk
1
0
1
−3
4
vk
0
1
−1
4
−5
rk
19
15
4
3
1
0
qk
0
1
3
1
3
A chaque étape, si on ne s’intéresse qu’au triplet Lk = [uk , vk , rk ] alors Lk+1 = Lk−1 − qk Lk . On
pourra donc coder [u,v,r] dans une liste.
● (M1) comme une liste de Python : dans ce cas, il faut expliquer à Python comment ajouter
deux listes et multiplier une liste par un nombre.
● (M2) comme un np.array : l’avantage est que numpy sait ajouter les listes et les multiplier
par un nombre.
2
L’algorithme d’exponentiation rapide
Le but de cette algorithme est de calculer la puissance N -ième xN d’un nombre x qui peut être
un entier ou un flottant, avec un minimum d’opérations.
L’intérêt de cet algo. ne se limite pas aux entiers. Si on l’introduit ici, c’est qu’ensuite on
l’appliquera aussi dans les anneaux de congruences, où il est encore plus efficace, et qu’on
s’en servira pour des problèmes d’arithmétiques...
a) L’idée essentielle : Si N = a0 + a1 2 ⋅ ⋅ ⋅ + an 2n , écriture en base deux, avec ai ∈ {0, 1} alors :
n
xN = xa0 (x2 )a1 . . . (x2 )an .
Ensuite deux points de vue possibles, cela dépend si on connait déjà l’écriture en base 2 de
N ou pas.
b) 1ère méthode (des poids faibles vers les poids forts)
En lisant le développement en base deux de droite à gauche : c’est le plus souvent comme cela
que l’algorithme est présenté, car dans ce cas, on peut aussi obtenir les chiffres successifs du
développement en base 2 à chaque étape par l’algorithme des poids faibles déjà vu au T.P. 5 :
pas besoin d’avoir fait le calcul du dév. en base 2 avant !
Pour mettre en oeuvre l’algorithme on utilise trois variables qu’on va appeler res et aux
comme résultat et auxiliaire et la variable N qui au départ contient comme valeur l’exposant
N et qui va permettre à chaque étape de calculer le chiffre du développement en base 2 de
N.
De manière pas complètement formelle, à l’étape i :
i
i−1
● On stocke dans aux la valeur de x2 , obtenue comme carré de x2
obtenu à l’étape
précédente.
● En même temps, on divise à chaque étape N par 2 pour connaı̂tre le i-ème chiffre ai de
son écriture en base 2. i.e. on itère N =N//2 . Le ai est le reste de la division euclidienne
de N par 2.
i
● Enfin toujours à l’étape i , si ai = 1, on multiplie res par x2 c’est-à-dire la valeur de aux
à l’étape i, sinon on ne change pas res.
1
Exercice à faire : implémenter cette idée d’algorithme en Python.
Rappel l’algo. de dév. en base deux des poids faibles vers les poids forts illustré
sur un exple :
24
=
2 × 12 + 0 → a0 = 0,
12
=
2 × 6 + 0 → a1 = 0,
6
=
2 × 3 + 0 → a2 = 0,
3
=
2 × 1 + 1 → a3 = 1,
1
=
2 × 0 + 1 → a4 = 1.
c) 2ème méthode (des poids forts vers les poids faibles)
En lisant l’écriture en base 2 de gauche à droite : cette méthode évite l’utilisation d’une
variable auxiliaire, mais nécessite d’avoir calculé le développement en base 2 de N : ce qui
n’est pas un gros problème pratique en Python, mais jouerait sur les questions de coût.
L’idée astucieuse, qui est utile dans d’autres contextes (algorithme de Hörner), est d’écrire :
xN = ((. . . (xan )2 xan−1 )2 . . . )xa1 )2 xa0
Par exemple avec 24 qui s’écrit 11000 en base 2 : x24 = (((x)2 .x)2 )2 )2 ).
On lit donc l’écriture en base 2 de N de gauche à droite en ignorant le premier 1, on part de
res := 1 et à chaque étape s’il y a un 1 dans le développement de x en base 2 on remplace
res par (res*res)*x, sinon on prend juste le carré i.e. on remplace res par (res*res).
Exercice à faire : implémenter cette idée d’algorithme en Python
d) Montrer que si N s’écrit avec n + 1 chiffres en base 2, alors cet algorithme permet de calculer
xN avec au plus 2n multiplications.
e) Comparer la vitesse de vos fonctions avec celle de la fonction pow(x,N) de Python qui utilise
ce même algorithme d’exponentiation rapide.
2
T.P. 8 : algorithmes et arithmétique (suite)
3
Le test de Fermat
3.1
Exponentiation modulaire
Pour expérimenter le test de Fermat, nous commençons par modifier l’algorithme d’exponentiation rapide pour le faire travailler dans les anneaux de congruences (Z/nZ, +, ×).
Ecrire une fonction exp_mod(x,N,m) qui applique l’algorithme d’exponentiaton rapide pour
calculer xN , en réduisant à chaque étape le résultat modulo m, autrement dit, en remplaçant le
résultat à chaque étape par son reste dans la division euclidienne par m.
Remarque : La fonction Python pow fait exactement cela si on lui rentre l’argument facultatif
m. (A condition de ne pas prendre celle du module math !)
3.2
Le test de Fermat
Le petit théorème de Fermat dit que si n est un nombre premier alors pour tout a ∈ N, an ≡ a [n].
Ce théorème fournit donc une C.N. pour qu’un nombre soit premier. Si on a un a tel que an ≡/ a [n],
on est sûr que n n’est pas premier. On dira que n ne passe pas le test de Fermat pour la valeur a
en question et donc n’est pas premier.
a) Ecrire une fonction test_Fermat qui prend en argument un nombre n et un argument
facultatif a, qui sinon admet a=2 comme valeur par défaut, et qui teste si n ≪ passe ≫ le test
de Fermat pour tous entiers entre 2 et a, en renvoyant un booléen.
b) Déterminer la liste des nombres non premiers entre 1 et 10000 qui passent le test de Fermat
pour a = 2 puis a = 3. On utilisera une fonction du type de la fonction premier_mieux du
cours, qu’on appellera simplement premier pour tester la primalité du nombre.
3.3
Fabrications d’entiers aléatoires avec de gros facteurs premiers
a) Ecrire une fonction alea qui prend comme argument facultatif un nombre n, avec comme
valeur par défaut n=500 et qui renvoie un nombre entier impair aléatoire à n chiffres.
b) Fabriquer à l’aide de la fonction précédente une liste de 10 nombres aléatoires à 300 chiffres
chacun et tester la primalité de chacun avec le test naı̈f de la fonction premier. Pour le
premier nombre pour lequel ce test prend beaucoup de temps arrêter la recherche et comparer
au test de Fermat pour 2, 3...
c) Fabriquer (de manière automatique !) une liste de 10 nombres pour lesquels la fonction
premier ne s’arrête pas avant un grand diviseur (à choisir) et appliquer le test de Fermat
pour ces nombres pour 2 et 3.
d) Tester l’algorithme d’Euclide avec deux nombres à 300 chiffres. Il répond souvent 1. Comparer
avec le temps de calcul de la D.F.P.
3.4
Nombre de Carmichael
Un nombre de Carmichael est un entier n ≥ 2 non premier qui vérifie la propriété que pour tout
entier a ∈ Z : an ≡ a [n].
A l’aide de la fonction précédente, écrire un programme qui donne la liste de tous les nombres
de Carmichael inférieurs à 10000.
3
4
Le principe du R.S.A (Appendice au T.P. 8)
4.1
Prérequis mathématiques : la fonction ϕ d’Euler
Nous avons démontré en exercice le résultat suivant :
Théorème : Si (G, )˙ est un groupe abélien fini à n éléments alors pour tout x ∈ G, on a xn = e.
Nous avions vu que ce théorème, appliqué à G = Z/pZ∗ pour p un nombre premier donne
immédiatement le petit théorème de Fermat. Nous allons voir ici une généralisation.
a) Pour tout n ∈ N∗ , on note ϕ(n) le nombre d’entiers k ∈ [[1, n]] tels que k ∧ n = 1.
i) Calculer ϕ(p) si p est premier.
ii) Relier ϕ(n) au nombre d’éléments inversibles de l’anneau (Z/nZ, +, ×) (avec dém.).
iii) En déduire la propriété suivante, due à Euler :
∀ a ∈ Z, ∀ n ∈ N∗ , a ∧ n = 1 ⇒ aϕ(n) ≡ 1 [n]
iv) Quel résultat connu la question précédente généralise-t-elle ?
b)
i) Montrer que si a ∧ b = 1 alors ϕ(ab) = ϕ(a)ϕ(b) (plus difficile !)
ii) Montrer que si pet q sont deux nombres premiers distincts, alors pour tout a premier
avec pq, on a a(p−1)(q−1) ≡ 1 [pq].
iii) On garde les hyp. du b).
Soit e ∈ N∗ premier avec (p − 1)(q − 1) et soit d ∈ N∗ tel que de ≡ 1 [(p − 1)(q − 1)].
Justifier l’existence de l’entier d et montrer que ade ≡ a [pq].
4.2
4.2.1
Cryptage de Rivest, Shamir, Adleman (1978)
La fabrication de la clé de codage : sa partie publique, sa partie secrète
Dans la méthode R.S.A., il y a deux personnes dont les rôles sont bien distincts. Nous parlerons
d’un chef et d’un subordonné, le subordonné devant transmettre des messages chiffrés (i.e. protégé
par un code secret) au chef.
Le chef choisit deux nombres premiers p et q (assez grands, disons de 150 chiffres chacun en
base 10). Il calcule le produit n = pq (c’est facile pour un ordinateur : pour des nombres de 100
chiffres, cela ne fait que 10000 opérations, et un ordi en fait 1011 par secondes), puis le nombre
(p − 1)(q − 1).
Il choisit ensuite un nombre e premier avec (p − 1)(q − 1).
Question 1 Pourquoi est-ce facile à faire ?
Il rend alors publique la clé R.S.A. i.e. les nombres n et e, mais surtout pas les nombres p et q.
Il calcule de son côté, un nombre d tel que de ≡ 1 [(p − 1)(q − 1)].
Question 2 Comment peut-on le faire en pratique ?
On notera que cela nécessite de connaı̂tre le nombre (p − 1)(q − 1) = pq − q − p + 1 (ce qui revient à
connaı̂tre p et q). Pour cela, si on n’est pas le chef, il suffirait de factoriser n. Mais, tout le principe
de la méthode R.S.A. est : il est trop compliqué de décomposer n en facteur premier, même pour
un ordinateur !
A ce stade, le chef a une clé secrète qui est (n, p, q, e, d) avec n = p × q et il a rendu publique
(pour quiconque veut lui envoyer des messages) une partie de la clé : (n, e).
4.2.2
Comment un subordonné peut envoyer des messages au chef ?
Le message est représenté par un nombre. Ce nombre est découpé en paquets de chiffres successifs, pour que chaque paquet définisse un nombre inférieur à l’entier n défini par la clé publique.
Pour chaque paquet m, le subordonné calcule m′ = me et envoie m′ au chef.
4.2.3
Comment le chef peut décoder le message reçu ?
Le chef est le seul à connaı̂tre d, il calcule m′d modulo n, et il retombe sur le message original
m.
4
Téléchargement