Algèbre, arithmétique (Mat309)

Bernard.Parisse@univ-grenoble-alpes.fr

2019

Table des matières

Index

  • équivalence, relation, 4.1.1

  • algorithme d’Euclide, 3.4
  • anneau, 3.2
  • anneau commutatif, 3.2
  • anneau euclidien, 3.2
  • anneau intègre, 3.2
  • anneau unitaire, 3.2
  • application lineaire, 5.2

  • Bézout, identité de, 3.5
  • base, 2.1.1, 5.2
  • base canonique, 5.2

  • canonique, base, 5.2
  • commutatif, anneau, 3.2
  • congruence, 4.1.1

  • dimension, 5.2
  • distance d’un code, 5.4.6
  • distance de Hamming, 5.4.6
  • division euclidienne, 3.1

  • Euclide étendu, 3.5
  • Euclide, algorithme, 3.4
  • Euler, indicatrice, 4.2
  • endomorphisme, 5.2
  • espace vectoriel, 5.2
  • euclidien, anneau, 3.2
  • euclidien, quotient, 2
  • euclidien, reste, 2
  • exponentiation rapide, 4.3

  • generatrice, 5.2
  • groupe, 3.2

  • Hamming, distance, 5.4.6
  • image, 5.2
  • indicatrice d’Euler, 4.2
  • intègre, anneau, 3.2
  • inversible, groupe, 4.2

  • Karatsuba, 2.2.3

  • libre, 5.2
  • lineaire, application, 5.2

  • noyau, 5.2

  • ordre partiel, 3.3
  • ordre d’un élément, 4.2
  • ordre, relation, 3.3

  • PGCD, 3.4
  • parfait, code correcteur, 5.4.7
  • parité (bit de), 5.4.2
  • partiel ordre, 3.3
  • plus grand diviseur commun, 3.4
  • puissance rapide, 4.3

  • quotient euclidien, 2

  • rapide, puissance, 4.3
  • relation d’ordre, 3.3
  • reste euclidien, 2

  • sous-espace vectoriel, 5.2
  • stasthme, 3.2

  • unitaire, anneau, 3.2

  • vectoriel, espace, 5.2
  • vectoriel, sous-espace, 5.2

onload

1  Motivations: cryptographie, codes correcteurs

La transmission d’informations par les réseaux sous forme numérique pose deux problèmatiques importantes

Ces deux problématiques ont des solutions à la frontière entre informatique et mathématiques. Dans ce cours intitulé Algèbre et arithmétique, on présente plusieurs structures algébriques (groupes, anneaux, corps, espaces vectoriels) ainsi que des algorithmes d’arithmétique dans ces structures, et on étudiera leur efficacité, qui assure la confidentialité des données transmises ou permet de détecter et corriger rapidement des erreurs de transmission (pas trop nombreuses).

1.1  Cryptographie

Historiquement, les militaires Romains pratiquaient déjà la cryptographie, avec le code dit de César qui consistait à décaler de plusieurs positions les lettres constituant un message dans l’alphabet, de manière cyclique. Par exemple si on décale de 4 positions, un A devient un E, un B devient un F, etc et un Z devient un D. Ce procédé est facile à implémenter sur une chaine de caractère numérisée (on additionne la clef de cryptage modulo 26) mais il est trop simple, car le nombre de possibilités de cryptage est trop limité (voir feuille d’exercices 1).
Plusieurs variations permettent de rendre ce système plus robuste. Par exemple on peut choisir une permutation quelconque des 26 lettres de l’alphabet. Cette méthode est toutefois sujette à une attaque statistique, toutes les lettres de l’alphabet n’ont pas la même fréquence dans un message (c’est pour cela qu’elles ne valent pas le même nombre de points au scrabble!).
Une autre méthode utilisée pendant la seconde guerre mondiale par les services de renseignements utilise une clef variable, on fait toujours une addition modulo 26, mais la clef change à chaque lettre, on utilise par exemple la position d’une série de lettre prise dans un livre, popularisé par The Key to Rebecca (Le Code Rebecca) de K. Follett basée sur l’histoire de l’espion nazi Johannes Eppler et l’opération Salaam. Ce type de code est incassable sans connaissance de la clef.
Une méthode plus élaborée était mise en oeuvre par la machine Enigma utilisée par les militaires nazis toujours pendant la seconde guerre mondiale, code qui a été cassé par Alan Turing et son équipe à Bletchley Park, popularisé par le film The Imitation Game et qui marque les débuts de l’informatique moderne (machine de Turing universelle).

On peut modéliser ces systèmes cryptographiques par la donnée d’une bijection EE d’un ensemble d’entiers et de sa bijection réciproque DD. Par exemple pour le cryptage de César, l’ensemble d’entiers est [0,25][0,25] (A=0, B=1, etc.), nn est le décalage fixe et E(x)=x+nmod26,D(x)=xnmod26E(x)=x+n \ \mbox{mod} \ 26, \quad D(x)=x-n \ \mbox{mod} \ 26 On peut imaginer des variations par exemple le codage dit linéaire E(x)=ax+bmod26E(x)=ax+b \ \mbox{mod} \ 26 qui est bijectif si aa est premier avec 26. Ces méthodes de cryptage sont dites à clef secrète (ou symétrique), car la connaissance des paramètres de EE permet de déterminer facilement DD. Il est nécessaire d’échanger la clef (par exemple par une rencontre physique) avant de pouvoir échanger des données.
L’essor des communications a nécessité l’invention de mécanismes permettant de sécuriser l’échange de données sans échanger au préalable des clefs. La méthode RSA (inventée en 1977 par Ronald Rivest, Adi Shamir et Leonard Adleman) très populaire aujourd’hui utilise le calcul de puissances d’entiers modulo le produit de deux grands nombres premiers, nous allons maintenant la présenter rapidement et nous y reviendrons plus en détails plus loin lorsque nous aurons tous les outils nécessaires pour l’analyser. On se donne donc nn, produit de deux grands nombres premiers pp et qq, nn est public, alors que pp et qq sont secrets. La sécurité repose sur le fait que multiplier de grands entiers est rapide (moins d’une seconde pour 1 millions de digits) alors que factoriser un entier (avec les algorithmes connus actuellement) prend beaucoup de temps dès qu’on dépasse la centaine de chiffres et est actuellement impossible au-delà d’environ 200 chiffres. Exemple avec pp et qq pas trop grands :



On se donne également ee et dd deux entiers qui vérifient la relation edmod(p1)(q1)=1ed=m(p1)(q1)+1(m)(1) ed \ \mbox{mod}\ (p-1)(q-1)=1 \quad \Leftrightarrow \quad ed=m(p-1)(q-1)+1 \ (m \in \mathbb{N}) \qquad (1) ee est public, alors que dd est secrète.
Exemple de paire de clefs :

Une donnée à sécuriser est représentée par un entier a<na&lt;n, la fonction de codage est b=E(a)=a emodnb=E(a)=a^e \ \mbox{mod}\ n ses paramètres sont publics donc n’importe qui peut coder une donnée. On montrera que la fonction de décodage est a=D(b)=b dmodna=D(b)=b^d \ \mbox{mod}\ n Seul le détenteur de dd peut décoder. On a donc la possibilité d’échanger des données de manière sécurisée sans avoir besoin d’échanger des clefs auparavant.
Illustration : on code les caractères ASCII du mot Bonjour puis on décode (attention, dans la réalité il ne faut surtout pas coder caractère par caractère de cette manière)

Le fait que les fonctions DD et EE soient réciproques l’une de l’autre est une conséquence du :
Petit théorème de Fermat : soit pp est un nombre premier, et aa un entier, alors a paa^p-a est divisible par pp.

On a a pa=a(a p11)a^p-a=a(a^{p-1}-1), donc si aa n’est pas un multiple de pp, on en déduit que a p11a^{p-1}-1 est divisible par pp puisque pp est premier. Donc a k(p1)1a^{k(p-1)}-1 est divisible par pp car x k1=(x1)(x k1+...+1)x^k-1=(x-1)(x^{k-1}+...+1) (prendre x=a p1x=a^{p-1}). En multipliant par aa, on conclut que pour tout aa entier, a k(p1)+1aa^{k(p-1)+1}-a est divisible par pp, donc a edaa^{ed}-a est divisible par pp (prendre k=m(q1)k=m(q-1) dans (1)). Pour les mêmes raisons a edaa^{ed}-a est divisible par qq, donc il est divisible par n=pqn=pq puisque pp et qq sont des nombres premiers distincts, CQFD.

Le petit théorème de Fermat peut se montrer par récurrence sur aa. Rappelons d’abord la formule du binôme : (a+b) p= k=0 pp!k!(pk)!a kb pk(a+b)^p=\sum_{k=0}^p \frac{p!}{k!(p-k)!} a^k b^{p-k} En effet quand on développe le produit, il y a autant de termes a kb pka^k b^{p-k} que de choix de kak\ a dans une liste de pp éléments, sans tenir compte de l’ordre. Si on tient compte de l’ordre, choisir kk éléments parmi pp laisse p(p1)...(pk+1)=p!(pk)!p(p-1)...(p-k+1) = \frac{p!}{(p-k)!} possibilités (pp choix pour le premier élément, p1p-1 pour le second, etc.). Il faut ensuite diviser par les ordres possibles entre les kk éléments choisis, i.e. k!k!.

Pour a=0a=0, a pa=0a^p-a=0 est bien divisible par pp. Pour montrer l’hérédité, on développe (a+1) p(a+1)^p avec la formule du binôme, (a+1) p= k=0 pp!k!(pk)!a k(a+1)^p=\sum_{k=0}^p \frac{p!}{k!(p-k)!} a^k tous les termes de la somme d’indices k=1k=1 à k=p1k=p-1 sont divisibles par pp, donc (a+1) p(a p+1)(a+1)^p-(a^p+1) est divisible par pp, l’hypothèse de récurrence nous donne a paa^p-a divisible par pp, on additionne et on conclut.

Remarque : si on développe (a+b) p+1=(a+b) p(a+b)(a+b)^{p+1}=(a+b)^p (a+b) en appliquant la formule du binôme des deux cotés, on obtient une formule donnant les coefficients binomiaux de la ligne p+1p+1 en fonction de ceux de la ligne pp (p+1 k)=(p k)+(p k1)\begin{pmatrix}p+1\\k\end{pmatrix} =\begin{pmatrix}p\\k\end{pmatrix} + \begin{pmatrix}p\\k-1\end{pmatrix} qui permet de calculer les coefficients binomiaux par un algorithme itératif simple.
Exercice : Implémenter cet algorithme, et vérifier pour quelques valeurs que tous les coefficients sauf le premier et le dernier d’une ligne d’indice pp premier sont divisibles par pp

La sécurité du cryptage est liée au temps de calcul de la factorisation de l’entier nn, produit de deux grands nombres premiers pp et qq relativement au temps de calcul du produit de ces deux nombres premiers pp et qq. Encore faut-il s’assurer qu’on peut coder et décoder en un temps raisonnable, c’est à dire calculer efficacement a emodna^e \ \mbox{mod}\ n (ou b dmodnb^d \ \mbox{mod}\ n c’est le même algorithme). Pour cela on utilise l’algorithme dit de la puissance modulaire (ou exponentiation modulaire) rapide. Il repose sur l’observation suivante :

Attention, pour rendre cet algoritme récursif efficace, il faut veiller à faire les calculs intermédiaires modulo nn. On effectue alors des multiplications d’entiers plus petits que nn, et le nombre de multiplications est de l’ordre du nombre de bits de mm au lieu de mm, c’est ce qui fait toute la différence (un millier d’opérations contre 10 30010^{300}), comme on le voit sur le calcul ci-dessous (avec mm beaucoup plus petit que 10 30010^{300} pour que l’algorithme lent se termine)

Dans la suite de ce cours, nous allons “industrialiser” ces preuves et méthodes, en étudiant l’arithmétique des entiers, des entiers modulo un entier, on parlera aussi de polynômes, de groupes commutatifs, d’anneaux euclidiens, de corps finis premiers, et un peu de temps de calcul.

1.2  Codes

La théorie de l’information et du codage est beaucoup plus récente que la cryptographie (dont l’intérêt militaire est évident), elle date des années 50 (Shannon, Hamming). Il ne s’agissait pas à l’époque de télécommunications mais de pallier à la fiabilité des cartes perforées.

L’idée est d’ajouter de la redondance, par exemple un code très simple consiste à répéter 3 fois chaque bit. Si en réception les 3 bits sont identiques, on considère que la valeur commune est la bonne, sinon il y a eu une erreur, et on peut la corriger sous l’hypothèse qu’il y a eu au plus une erreur, en votant à la majorité. Mais ce système est peu efficace si la probabilité d’erreur sur 1 bit est faible, car il utilise 3 fois plus de bande passante qu’il n’est nécessaire pour transmettre l’information sur un canal 100% fiable.

On verra vers la fin du cours des méthodes permettant de détecter et corriger des erreurs avec une efficacité meilleure, ces méthodes ont besoin de structure qui sera apportée par les espaces vectoriels dont les vecteurs ont des coordonnées dans {0,1}\{0,1\} au lieu de ,\mathbb{Q}, \mathbb{R} ou \mathbb{C} (on se limite à /2\mathbb{Z}/2\mathbb{Z} dans le cadre de ce cours). On parle de codes linéaires. Par exemple, le code de Hamming binaire 7,4 encode un quartet (i.e. un 4-uplet de bits) xx par un 7-uplet y=Gxy=Gx, en multipliant modulo 2 par la matrice GG le vecteur xx :

On observe que les 4 premières lignes de GG forment la matrice identité ce qui permet facilement de reconstituer xx si yy a été transmis correctement x=y[0:3]

Pour détecter une erreur, on vérifie que yy est dans le noyau de la matrice

S’il n’y a pas eu d’erreur détectable, alors Hy=0Hy=0 (modulo 2). S’il y a une erreur au plus, alors HyHy est à une permutation près l’écriture en base 2 du numéro de composante de yy à modifier.

Par exemple, on simule une erreur de transmission en changeant le bit d’indice nn :

Ici s mod 0 a pour effet d’enlever les % pour avoir une liste de 0 et de 1, que l’on convertit en entier et à qui on applique la permutation inverse de pp, on retrouve bien l’indice de l’erreur (vous pouvez le vérifier en changeant la valeur de nn).

2  Arithmétique des entiers et polynômes.

Définition 1   Si aa et bb sont deux entiers naturels, b0b \neq 0, on définit le quotient euclidien de aa par bb comme le plus grand entier naturel qq tel que abq0a-bq \geq 0 (l’ensemble des entiers naturels tels que abq0a-bq \geq 0 est borné par aa donc admet un élément maximal), On définit rr le reste euclidien de aa par bb par r=abqr=a-bq. On vérifie que r[0,b1]r \in [0,b-1] (sinon ab(q+1)a-b(q+1) serait positif ou nul).

2.1  Petits et grands entiers

Les microprocesseurs sont capables d’effectuer des opérations arithmétiques de base (toujours +,-, pratiquement toujours * et la division euclidienne) sur des petits entiers compris dans l’intervalle [2 t1,2 t11][-2^{t-1},2^{t-1}-1] (entier signé) ou [0,2 t1][0,2^{t}-1] (entier non signé), pour t=8t=8, t=16t=16, t=32t=32 et t=64t=64 (parfois aussi pour t=128t=128). En cas de dépassement, les calculs sont faits modulo 2 t2^t. Les logiciels de calcul doivent pouvoir travailler avec des entiers de taille plus grande, il faut les stocker en mémoire et implémenter les opérations arithmétiques de base en se ramenant à des petits entiers. Pour faire cela, on va écrire des entiers dans des bases adéquates.

2.1.1  Ecriture en base bb

Il s’agit de généraliser l’écriture des entiers qui nous est familière en base 10 à une base quelconque, les ordinateurs préférant la base 2 ou une puissance de 2. Ainsi écrire 1602 signifie que 1602=1×10 3+6×10 2+0×10 1+21602=1 \times 10^{3} + 6 \times 10^2 + 0 \times 10^1 + 2 La taille d’un nombre est le nombre de chiffres qu’il contient, ainsi 1602 s’écrit avec 4 chiffres (en base 10). Les nombres à 4 chiffres sont compris entre 1000=10 31000=10^3 et 100001=10 4110000-1=10^4-1, plus généralement, un nombre compris entre 10 n110^{n-1} et 10 n110^{n}-1 sécrit avec nn chiffres. Si on change pour une base b2b \geq 2 quelconque, on a alors :

Théorème 2   On se fixe une fois pour toutes un entier b2b\geq 2. Soit NN un entier naturel non nul, on peut alors écrire NN de manière unique sous la forme N= i=0 nN ib i,N i[0,b1],N n0(2) N=\sum_{i=0}^n N_i b^i , \quad N_i \in [0,b-1], N_n \neq 0 \qquad (2) Les N iN_i sont appelés bits si b=2b=2 ou digits si b=10b=10 (parfois aussi pour b>2b&gt;2, par exemple b=16b=16).
On a
n=n=floor(log(N)log(b)\frac{\log(N)}{\log(b)})floor est la partie entière (plus grand entier inférieur ou égal). (Attention, le nombre de chiffres de NN est n+1n+1).

On remarque que le nombre nn de digits pour écrire NN en base bb est floor(log b(N)\log_b(N)), en effet d’une part : b nN nb nNb^n \leq N_n b^n \leq N et d’autre part : N< i=0 nN ib i(b1) i=0 nb i=b n+11<b n+1N &lt; \sum_{i=0}^{n} N_i b^i \leq (b-1) \sum_{i=0}^{n} b^i = b^{n+1} -1 &lt; b^{n+1} puis on prend les log.

Pour montrer l’unicité de l’écriture, on suppose qu’il y a deux écritures distinctes de ce type, et on regarde le coefficient d’indice maximal qui est différent (appelons le jj), on a alors (N˜ jN j)b j= i=0 j1(N iN˜ i)b i(\tilde{N}_j-N_j)b^j = \sum_{i=0}^{j-1} (N_i-\tilde{N}_i)b^i quitte à changer de signe, on peut supposer que le membre de gauche est strictement positif, on a alors (N jN˜ j)b jb j(N_j-\tilde{N}_j)b^j \geq b^j Mais le membre de droite se majore par i=0 j1(b1)b j=b j1\sum_{i=0}^{j-1} (b-1) b^j=b^j-1 absurde.

L’existence se prouve par l’algorithme suivant qui utilise la division euclidienne (on a utilisé irem(N,b) au lieu de N % b pour calculer le reste de division euclidienne pour des raisons de compatibilité avec LATEX)

def ecriturebase(N,b):
    L=[]
    while N>0:
        L.append(irem(N, b))
        N=N//b
    return L

onload

i.e. tant que N>0N&gt;0, on calcule le reste de NN par bb, qui donne le coefficient de poids le plus faible de l’écriture de NN en base bb, on ajoute lécriture en base bb du quotient de NN par bb. L’algorithme s’arrête au bout de partie entière de log b(N)+1\log_b(N)+1 itérations. Réciproquement, on vérifie que l’écriture obtenue convient en développant N 0+b(N 1+b(...+b(N n1+b(N n))...))(3) N_0+b(N_{1}+b(...+b(N_{n-1}+b(N_n))...)) \qquad (3) On observe au passage que l’écriture de NN sous la forme ci-dessus nécessite nn additions et nn multiplications, et est donc plus efficace que le calcul sous la forme développée 2. C’est la méthode dite de Hörner.

Exemple : en base b=2b=2. Pour écrire N=12N=12 en base 2, on calcule le reste de 12 par 2 donc N 0=0N_0=0, le quotient de 12 par 2 vaut 6, on divise par 2, reste 0 donc N 1=0N_1=0, quotient 3, on divise par 2, reste N 2=1N_2=1, quotient 1, on divise par 2, reste N 3=1N_3=1 quotient 0 on s’arrête, donc 12=0b1100. Réciproquement on a bien 0+2×(0+2×(1+2×(1)))=120 +2\times(0+2\times(1+2\times(1)))=12

Exercice : la commande b:=convert(N,base,b) de Xcas effectue la conversion d’un entier NN en la liste des coefficients de son écriture en base bb, la réciproque étant convert(L,base,b) ou horner(L,b). Implémenter des fonctions équivalentes dans votre langage de programmation préféré.

Exercice : comment passe-t-on simplement de la représentation d’un nombre en base 2 à un nombre en base 16 et réciproquement ?

2.1.2  Lien avec les polynômes de A[X]A[X] avec A=A=\mathbb{Z}.

A tout entier naturel NN, on peut associer un polynôme à coefficients entiers qui est son écriture en base bb, les coefficients du polynôme sont dans l’intervalle [0,b1][0,b-1]. Réciproquement, si les coefficients d’un polynôme sont des entiers compris entre [0,b1][0,b-1] alors ils correspondent à l’écriture d’un entier en base bb.

On va voir que les opérations arithmétiques de base sur les grands entiers reviennent à effectuer des opérations de base sur les polynômes, avec une petite difficulté supplémentaire, il faut tenir compte de retenues. Les algorithmes naifs pour additionner et multiplier deux polynômes correspondent précisément aux algorithmes enseignés à l’école primaire pour effectuer l’addition ou la multiplication de deux entiers.

On travaille sur ordinateur avec une base bb qui est une puissance de 2, par exemple on pourrait prendre b=2 8b=2^8 et dans ce cas les entiers s’écrivent avec des chiffres qui sont des octets. Ainsi \nAa représente 13*256 2+65*256+9713*256^2+ 65*256+97 En pratique, sur des processeurs 32 bits, on utilise b=2 32b=2^{32} les chiffres sont des double-mots (dword), sur des processeurs 64 bits b=2 64b=2^{64} les chiffres sont des quadruple mots (qword).

2.2  Opérations arithmétiques de base

2.2.1  Addition, soustraction

Si N= i=0 nN ix iN=\sum_{i=0}^n N_i x^i et M= i=0 mM ix iM=\sum_{i=0}^m M_i x^i sont deux polynômes, alors N+M= i=0 max(n,m)(N i+M i)x i,N+M=\sum_{i=0}^{\mbox{max}(n,m)} (N_i+M_i) x^i, Si N= i=0 nN ib iN=\sum_{i=0}^n N_i b^i et M= i=0 mM ib iM=\sum_{i=0}^m M_i b^i sont deux entiers écrits en base bb, alors N+M= i=0 max(n,m)(N i+M i)b i,N+M=\sum_{i=0}^{\mbox{max}(n,m)} (N_i+M_i) b^i, il faut donc additionner les digits de l’écriture en base bb comme pour additionner deux polynômes. On a N i+M i[0,2b2]N_i+M_i \in [0,2b-2] , si N i+M i<bN_i+M_i&lt;b, il n’y a rien à faire, sinon on remplace par N i+M ibN_i+M_i-b et on ajoute 1 (retenue) à N i+1+M i+1N_{i+1}+M_{i+1} qui appartient à [0,2b2+1][0,2b-2+1], etc. Le nombre d’opérations à effectuer est de l’ordre du maximum du nombre de bits/chiffres/digits de NN et MM.

2.2.2  Multiplication

Si N= i=0 nN ib iN=\sum_{i=0}^n N_i b^i et M= j=0 mM jb iM=\sum_{j=0}^m M_j b^i, alors on se ramène à la multiplication de deux polynômes : N×M= i=0 n j=0 mN iM jb i+jN\times M=\sum_{i=0}^n \sum_{j=0}^m N_i M_j b^{i+j} Si b>2b&gt;2, par exemple b=10b=10 ou b=16b=16, N iM jN_i M_j sera très souvent supérieur à bb, il y aura souvent des retenues! On peut regrouper les termes b i+jb^{i+j} ayant le même exposant, en utilisant des décalages pour tenir compte de b ib^i (c’est l’algorithme de multiplication posée en base 10) ou additionner au fur et à mesure N iM jN_i M_j au coefficient actuel de b i+jb^{i+j} (exercice de la feuille de TD). En base 2, si on pose la multiplication comme en base 10, il est conseillé d’effectuer les additions une par une, sinon la prise en compte des retenues est délicate.

Le nombre de multiplications et d’additions est de l’ordre de nmnm (on peut montrer que c’est aussi le cas en tenant compte des retenues, car si une retenue se propage de rr positions dans la boucle intérieure en jj, elle ne pourra pas se propager de plus de 2 positions pour les r2r-2 itérations suivantes de la boucle intérieure).

Taille du résultat : si on multiplie un nombre à 3 chiffres par un nombre à 2 chiffres, le résultat aura 4 ou 5 chiffres, penser par exemple à 412×11412 \times 11 ou 412×31412 \times 31. Dans le cas général, b nN<b n+1,b mM<b m+1b n+mNM<b n+m+2b^n\leq N&lt;b^{n+1}, \quad b^m \leq M&lt;b^{m+1} \quad \Rightarrow b^{n+m} \leq N M &lt; b^{n+m+2} donc le produit NMNM a pour taille n+m+1n+m+1 ou n+m+2n+m+2 bits/chiffres/digits.

Si nn et mm sont proches, par exemple égaux, le résultat a pour taille 2n2n alors que l’algorithme présenté nécessite n 2n^2 opérations. On peut donc espérer l’existence d’algorithmes plus efficaces (un peu comme les algorithmes de tri...). Nous allons en présenter un dans le cadre plus simple de la multiplication des polynômes (où on n’a pas à gérer les retenues).

2.2.3  Diviser pour régner: multiplication de Karatsuba.

Soient P,QP, Q deux polynômes de degrés strictement inférieur à 2n2n. On suppose que le cout d’une opération arithmétique dans l’ensemble des coefficients vaut 1 et on néglige les autres opérations (on suppose par exemple que l’ensemble des coefficients est fini). On écrit P=A+x nB,Q=C+x nD P=A+x^n B, \quad Q=C+x^n D avec A,B,C,DA,B,C,D de degrés strictement inférieur à nn, on a alors : PQ=AC+x n(AD+BC)+x 2nBDP Q = AC + x^n(AD+BC)+x^{2n} BD Il y a 4 produits de polynômes de degrés <n&lt;n, mais au prix d’additions intermédiaires, on peut se ramener à 3 produits, en effet (A+B)(C+D)ACBD=AD+BC(A+B)(C+D)-AC-BD = AD+BC donc pour calculer le facteur de x nx^n il suffit de soustraire à (A+B)(C+D)(A+B)(C+D) les produits ACAC et BDBD que l’on doit calculer par ailleurs. Au premier ordre, le temps nécessaire pour multiplier les 2 polynômes de degré <2n&lt;2n est multiplié par 3, au lieu de 4.

Plus précisément, soit M(n)M(n) le temps nécessaire pour calculer le produit de 2 polynômes par cette méthode, on a alors M(2n)=3M(n)+8nM(2n) = 3M(n)+ 8n 8n8n représente le nombre d’additions ou de soustractions pour former A+BA+B, C+DC+D, soustraire ACAC et BDBD, et tenir compte du fait que les termes de degré n\geq n de ACAC se combinent avec ceux de degré <2n&lt;2n de AD+BCAD+BC et les termes de degré <3n&lt; 3n de x 2nBDx^{2n}BD avec ceux de degré 2n\geq 2n de AD+BCAD+BC. On en déduit u n=M(2 n),u n+1=3u n+8×2 nu_n=M(2^n), \quad u_{n+1}=3u_n+8 \times 2^n cette récurrence se résoud facilement par la commande


ce qui donne M(2 n)=u n=82 n+93 nM(2^n)=u_n=-8\cdot 2^{n}+9\cdot 3^{n}.

Asymptotiquement, M(2 n)93 nM(2^n) \approx 9\cdot 3^{n} ce qui est bien meilleur que la multiplication naive en 24 n2 \cdot 4^n, mais pour de petites valeurs de nn, la multiplication naive est plus rapide comme l’illustre le graphe ci-dessous (en rouge la multiplication naive, en bleu Karatsuba complet) :
On utilise Karatsuba (récursivement) uniquement pour des valeurs de nn suffisamment grandes (théoriquement lorsque 8n8n, le surcout dû aux additions est plus petit que la multiplication économisée, soit 8n<2n 28n&lt;2n^2 soit n>4n&gt;4, en pratique plutôt pour nn de l’ordre de quelques dizaines selon les implémentations, car nous n’avons tenu compte que des opérations arithmétiques).

Exercice : vérifier que la multiplication des entiers longs en langage Python se comporte pour nn grand comme la multiplication de Karatsuba.

Les logiciels de calcul intensif utilisent la plupart du temps des algorithmes plus efficaces lorsque nn est encore plus grand, qui se rapprochent de nln(n)n \ln(n). C’est en particulier le cas de tous les logiciels de calcul formel qui utilisent la libraire GMP écrite en langage C (Xcas, Sagemath, Maple, Mathematica, ...). Ces algorithmes utilisent la transformation de Fourier rapide (FFT) en calcul exact. Voir par exemple (en anglais) ce lien.

2.3  Opérations de base sur les petits entiers.

Les petits entiers sont représentés en base b=2b=2, les opérations de base sont directement faites par le microprocesseur en se ramenant aux calculs en base 2:

2.4  Opérations de base sur les grands entiers

Les grands entiers naturels sont représentés dans une base qui est une puissance de 2, b=2 tb=2^{t'}, les coefficients de l’écriture étant de petits entiers, ils sont stockés en mémoire à des adresses consécutives. On utilise le plus souvent t=tt'=t la taille des régistres du microprocesseur, parfois une valeur légèrement inférieure pour simplifier la gestion des retenues.

Les implémentations de la multiplication font intervenir des algorithmes plus efficaces (comme Karatsuba), ainsi que des découpages par blocs visant à minimiser les opérations de lecture/écriture en mémoire au profit de l’utilisation de régistres et de l’optimisation du stockage dans la mémoire cache (L1). Avec les processeurs modernes, le cout du calcul d’un produit de 2 grands entiers par la méthode naive est dominé par les accès mémoire en RAM et non par les opérations arithmétiques sur les coefficients.

Expérience avec un langage compilé, ici C++, pour la multiplication de deux polynômes par la méthode naïve.

#include <iostream>
#include <vector>
using namespace std;

template<class T> ostream & operator << (ostream & os,const vector<T> & v){
  if (v.empty()) return os << "[ ]";
  const T * ptr=&v.front(),*ptrend=ptr+v.size();
  os << "[";
  for (;ptr!=ptrend;++ptr){
    os << *ptr << " ";
  }
  return os << "]";
}

template<class T>
void mul1(vector<T> & A,const vector<T> & M,const vector<T> & N){
  A=vector<T>(M.size()+N.size()-1);
  for (size_t i=0;i<M.size();++i){
    T Mi(M[i]);
    for (size_t j=0;j<N.size();++j){
      A[i+j] += Mi*N[j]; // 1 lecture et 1 ecriture 
    }
  }
}

template<class T>
void mul2(vector<T> & A,const vector<T> & M,const vector<T> & N){
  A=vector<T>(M.size()+N.size()-1);
  size_t i;
  for (i=0;i+3<M.size();i+=4){
    T M0=M[i], M1=M[i+1], M2=M[i+2], M3=M[i+3];
    T * Aptr=&A[i];
    size_t j=0;
    for (j=0;j+3<N.size();Aptr+=4,j+=4){
      T N0=N[j]; 
      T N1=N[j+1]; 
      T N2=N[j+2]; 
      T N3=N[j+3]; 
      *Aptr +=   M0*N0; 
      Aptr[1] += M1*N0+M0*N1;
      Aptr[2] += M2*N0+M1*N1+M0*N2;
      Aptr[3] += M3*N0+M2*N1+M1*N2+M0*N3;
      Aptr[4] +=       M3*N1+M2*N2+M1*N3;
      Aptr[5] +=             M3*N2+M2*N3;
      Aptr[6] +=                   M3*N3;
    }
    for (;j<N.size();++Aptr,++j){
      T Nj=N[j]; 
      *Aptr += M0*Nj; 
      Aptr[1] += M1*Nj;
      Aptr[2] += M2*Nj;
      Aptr[3] += M3*Nj;
    }
  }
  // fin du produit
  for (;i<M.size();++i){
    T Mi(M[i]);
    for (size_t j=0;j<N.size();++j){
      A[i+j]+=Mi*N[j];
    }
  }
}

int main(int argc,char ** argv){
  if (argc<2){
    cout << "Syntaxe " << argv[0] << " <entier> [methode 1 ou 2]" << endl;
    return 1;
  }
  int n=atoi(argv[1]);
  int algo=argc==3?atoi(argv[2]):1;
  vector<int> A,M(n),N(n);
  // on remplit M et N
  for (int i=0;i<n;++i){
    M[i]=i+2;
    N[i]=i+1;
  }
  cout << M << "*" << N << endl;
  if (algo==1)
    mul1(A,M,N);
  else
    mul2(A,M,N);
  cout << A << endl;
  return 0;
}

Compilation (sans utiliser les optimisations SSE) avec c++ -O2 -mno-sse prog1.cc. Appel de l’algorithme non optimisé, puis optimisé, puis vérification par exemple par
time ./a.out 100000 1 >& log1
time ./a.out 100000 2 >& log2
diff -q log1 log2
On gagne presque un facteur 2 en temps.

La fonction mul1 effectue une lecture et une incrémentation par produit de coefficients, donc nmnm lecture et nmnm incrémentation où nn et mm désignent le nombre de coefficients des polynômes NN et MM. La fonction mul2 stocke dans des régistres 4 coefficients de MM et de NN pour faire leur produit, elle effectue moins de la moitié d’opérations de lecture/écriture en RAM (asymptotiquement 4 lectures et 7 incrémentations pour 16 produits de coefficients donc nm/4nm/4 lectures et 7nm/167nm/16 incrémentations). Le nombre d’opérations arithmétiques sur les coefficients est identique.

2.5  Points à retenir absolument

3  Euclide

Dans cette section, on se concentre sur tout ce qui tourne autour de la division euclidienne.

3.1  Division euclidienne.

On a vu la définition de la division euclidienne pour les entiers naturels a=bq+r,r[0,b1]a=bq+r, r\in[0,b-1]. Comment calcule-t-on la représentation dans une base (que l’on notera β\beta) de qq et rr ? Si a<ba&lt;b alors b=0b=0 et r=ar=a. Si aba\geq b, le calcul de qq et rr en base β\beta peut se faire par l’algorithme dit de la potence.

On suppose donc aba \geq b. Posons a= j=0 na jβ j,b= j=0 mb jβ j,nm,q= j=0 lq jβ ja=\sum_{j=0}^n a_j \beta^j, \quad b=\sum_{j=0}^m b_j \beta^j, \quad n \geq m, \quad q=\sum_{j=0}^l q_j \beta^j On observe que l’écriture de qq en base β\beta n’a pas de digit d’indice ll supérieur strict à nmn-m, car abβ nm+1<0a-b \beta^{n-m+1}&lt;0. La valeur du digit q nmq_{n-m} d’indice nmn-m de qq (digit éventuellement nul) est le plus grand entier q nm[0,β1]q_{n-m}\in[0,\beta-1] tel que abq nmβ nm0a-b q_{n-m}\beta^{n-m} \geq 0. Comme j=0 nm1a jβ j<β nm\sum_{j=0}^{n-m-1} a_j \beta^j &lt; \beta^{n-m} cela revient à chercher le plus grand digit tel que j=nm na jβ jbq nmβ nm0\sum_{j=n-m}^n a_j \beta^j - b q_{n-m}\beta^{n-m} \geq 0 on regarde donc les m+1m+1 digits de aa les plus significatifs et on calcule le quotient euclidien par bb. Si q nm0q_{n-m}\neq 0, on effectue alors la soustraction a˜= j=0 na jβ jbq nmβ nm= j=0 nm1a jβ j+β nm( j=nm na jβ j(nm)bq nm)\tilde{a}=\sum_{j=0}^n a_j \beta^j - b q_{n-m}\beta^{n-m}= \sum_{j=0}^{n-m-1} a_j \beta^j +\beta^{n-m}(\sum_{j=n-m}^n a_j \beta^{j-(n-m)} - b q_{n-m} ) Pour déterminer le digit q nm1q_{n-m-1}, on recommence la procédure ci-dessus, avec les au plus m+2m+2 digits de a˜\tilde{a} d’indices supérieurs ou égaux à nm1n-m-1.

Le cout de l’algorithme de la potence est proportionnel au produit (nm+1)(m+1)(n-m+1)(m+1), le cas le pire correspond à mm proche de n/2n/2 qui donne un cout proportionnel à n 2n^2 (dans ce cas, on peut accélérer par un algorithme diviser pour régner de type Karatsuba).

Pour les entiers relatifs, d’autres conventions de signe sont possibles pour qq et rr. Ainsi, la division euclidienne en langage C renvoie qq tronqué vers 0. Si b>0b&gt;0, a % b sera du signe de aa. Pour obtenir le reste dans [0,b[[0,b[ lorsque a<0a&lt;0 (convention de signe de reste positif), il faudra ajouter bb à rr et diminuer qq de 1.

Optimisation : sur les microprocesseurs modernes, on évite si possible l’utilisation de tests pour optimiser le temps de calcul. En C, si on divise par b>0b&gt;0, et si aa est de signe quelconque, tous deux des entiers 31 bits, alors unsigned(a)>>31 vaut 0 si a0a\geq 0 et 1 sinon, on peut donc écrire :

inline int reste(int a,int b){
  return a % b + (unsigned(a)>>31)*b;
}

On peut aussi adopter la convention dite du reste symétrique, c’est-á-dire que r]|b|2,|b|2]r \in ]-\frac{|b|}{2},\frac{|b|}{2}]. En général, on peut supposer que b>0b&gt;0, ce qui donne le programme suivant :

def rsym(a,b):
    r = irem(a, b)
    if r<0:
        r += b
    if r<=b//2: 
        return r
    return r-b

onload

Optimisation :

int rsym(int r,int m){
  r %= m;
  r += (unsigned(r)>>31)*m; // rend r positif
  return r-(unsigned((m>>1)-r)>>31)*m;
}

Le reste de division euclidienne (%) est ici l’opération la plus couteuse, il existe heureusement parfois des méthodes qui permettent de l’éviter, par exemple on verra plus bas l’algorithme de PGCD binaire ou lorsqu’on fait des calculs modulo un entier fixé.

Pour les polynômes en une variable, si AA et BB sont deux polynômes à coefficients dans un corps KK, il existe un unique polynôme QQ et un unique polynôme RR tels que : A=BQ+R,deg(R)<deg(B)A=BQ+R, \quad \mbox{deg}(R)&lt;\ \mbox{deg}(B) L’unicité vient du fait que RR=B(QQ)R-R'=B(Q-Q') est incompatible avec les degrés si QQQ \neq Q'. L’existence se prouve par récurrence sur la différence de degré entre AA et BB, si elle est négative Q=0,R=AQ=0, R=A, si elle est nulle QQ est le quotient des coefficients dominants A aA_a de AA de degré aa et B bB_b de BB de degré bb, sinon, on pose Q=x abA a/B b+...Q=x^{a-b} A_a/B_b+..., ce qui annule le coefficient dominant de AA et on est ramené à une différence de degré diminuée de 1 (au moins).

Remarque 1 : si AA et BB sont à coefficients entiers et si BB est unitaire, alors QQ et RR sont à coefficients entiers. Sinon, on peut définir la pseudo-division de AA par BB en multipliant AA par le coefficient dominant de BB à la puissance la différence des degrés + 1.

Remarque 2 : pour les polynômes à plusieurs variables, il n’existe malheureusement pas de division euclidienne.

3.2  Anneau euclidien

L’ensemble des entiers relatifs \mathbb{Z} est muni des opérations + et * qui en font un anneau commutatif unitaire intègre. On rappelle (ou on définit) que

L’ensemble des entiers relatifs est donc un anneau commutatif unitaire intègre, mais il a une propriété supplémentaire, il possède une opération de division euclidienne pour aa\in \mathbb{Z} et bb non nul, il existe qq et rr (uniques) tels que a=bq+ra=bq+r avec r[0,b[r \in [0,b[. Cette propriété est également vraie pour les polynômes à coefficients dans un corps avec une condition de degré A=BQ+RA=BQ+R avec degree(R)(R)<degree(B)(B). Plus généralement, on parle de stasthme pour désigner l’équivalent du degré pour un polynôme ou de la valeur absolue de l’entier dans \mathbb{Z}.

3.3  Divisibilité

Définition 1   On dit que bb divise aa (ou que aa est divisible par bb) si le reste de la division de aa par bb est 0.

Ceci s’applique aussi bien aux entiers qu’aux polynômes. On observe que l’entier 0 est divisible par tous les entiers non nuls, et le polynôme nul par tous les polynômes non nuls. Si aa et bb sont des entiers naturels non nuls et si aa est divisible par bb alors aba\geq b. En effet a=bqa=bq avec q1q\geq 1 puisque a0a\neq 0. Si AA et BB sont deux polynômes non nuls et si AA est divisible par BB alors le degré de AA est supérieur ou égal au degré de BB.

Observation : 1 n’est divisible que par 1 et -1, les seuls entiers ayant un inverse pour la multiplication dans \mathbb{Z} sont 1 et -1. Pour les polynômes à coefficients dans un corps KK, le polynôme 1 est divisible par les polynômes constants non nuls (ceci est une conséquence du degré d’un produit qui est égal à la somme des degrés des facteurs), les polynômes constants sont les seuls polynômes ayant un inverse pour la multiplication dans les polynômes. Ces éléments, appelés inversibles de l’anneau, forment un groupe, ils jouent un role important, en effet si bb divise aa, i.e. a=bqa=bq alors pour tout élément inversible ii, bibi divise aa puisque a=(bi)i 1qa=(bi) i^{-1}q. Dans le cas des entiers, cela signifie qu’on peut négliger le signe pour étudier la divisibilité, autrement dit on peut se limiter aux entiers naturels. Dans le cas des polynômes, on peut multiplier par n’importe quel coefficient non nul, autrement dit on peut se limiter aux polynômes unitaires (i.e. dont le coefficient dominant est 1).

La relation de divisibilité dans l’ensemble des entiers naturels non nuls vérifie les propriétés suivantes :

Ces propriétés sont aussi vérifiées par la relation \leq sur les entiers (ou les réels). Une relation réflexive (aRaa R a), antisymétrique (aRba R b et bRab R a entraine a=ba=b) et transitive (aRba R b et bRcb R c entraine aRca R c) est appelée relation d’ordre. Il s’agit ici d’une relation d’ordre partiel, il y a des éléments qui ne peuvent être comparés, par exemple 2 et 3. Lorsque deux éléments sont toujours comparables, on parle de relation d’ordre total (par exemple \leq sur les entiers ou les réels ou l’ordre lexicographique sur les chaines de caractères ou les listes).

La divisibilité sur les polynômes unitaires est aussi une relation d’ordre partiel.

On verra plus loin un autre type de relation, les relations d’équivalence, où on remplace l’antisymétrie par la symétrie (aRba\ R\ b entraine bRab\ R\ a).

3.4  Le PGCD

Soient aa et bb deux entiers. On se ramène au cas des entiers naturels en enlevant les signes.

Définition 2   On définit le PGCD (plus grand commun diviseur) de deux entiers naturels aa et bb non simultanément nuls comme le plus grand entier naturel dd qui divise simultanément aa et bb. Si aa et bb sont nuls, on peut adopter comme convention que leur PGCD est nul.

Si l’un des deux éléments est nul, disons bb, alors la réponse est aa. Sinon, comme 1 divise aa et de bb, l’ensemble des diviseurs communs à aa et bb est non vide, et son plus grand élément est inférieur ou égal au minimum de aa et de bb, le PGCD existe donc bien.

On verra dans la suite que le PGCD intervient souvent en arithmétique. On peut déjà citer une de ses applications qui est la réduction des fractions sous forme irréductible.

Pour le calculer, on observe que le PGCD de aa et de bb est le même que celui de bb et de rr, le reste de la division euclidienne de aa par bb. En effet si dd est un diviseur commun à aa et bb, alors a=daa=da' et b=dbb=db' donc r=abq=d(aqb)r=a-bq=d(a'-qb'). Réciproquement si dd est un diviseur commun à bb et rr, alors b=dbb=db' et r=drr=dr' donc a=bq+r=d(bq+r)a=bq+r=d(b'q+r'). Cet algorithme, appelé algorithme d’Euclide, se traduit en un programme récursif (on a utilisé irem(a,b) pour le reste euclidien au lieu de a % b pour raison de compatibilité avec LATEX) :

def pgcdr(a,b):
     if b==0: 
          return a
     return pgcdr(b,irem(a,b))

onload

A chaque appel récursif, la valeur du reste diminue au moins de 1 puisque r<br&lt;b. Au bout d’un nombre fini d’appels, b=0b=0 et on a le PGCD. Si on utilise la valeur absolue du reste symétrique au lieu du reste euclidien positif, le reste perd (au moins) un bit dans son écriture en base 2 à chaque itération, le nombre d’itérations NN est au plus égal au minimum du nombre de bits de aa et bb.

Notons r 0=a,r 1=b,..,r Nr_0=a, r_1=b, .., r_N la suite des restes de l’algorithme d’Euclide (avec r N=0r_N=0 le dernier reste), on a r k=q kr k+1+r k+2r_k=q_k r_{k+1} + r_{k+2} avec un cout pour cette division proportionnel à #q k#r k+1\#q_k \#r_{k+1}#a\#a est le nombre de digits de l’écriture de aa. Or à une unité près #q k=#r k#r k+1\#q_k=\#r_k-\#r_{k+1}, on a donc un cout total pour l’algorithme d’Euclide proportionnel à k=0 N1(#r k#r k+1+1)#r k+1#b(N+ k(#r k#r k+1)#b(N+#a)\sum_{k=0}^{N-1} (\#r_k-\#r_{k+1}+1) \#r_{k+1} \leq \#b(N+\sum_k (\#r_k-\#r_{k+1}) \leq \#b(N+\#a) Donc le cout est en O(n 2)O(n^2) si le nombre de bits de aa et bb est plus petit que nn, en majorant le nombre d’étapes NN par nn avec la version restes symétriques d’Euclide. C’est encore vrai à une constante près sans prendre les restes symétrique mais un peu plus long à justifier, on peut par exemple utiliser le bonus de l’exercice sur Fibonacci F nF_n de la feuille de TD et un équivalent de F nF_n pour nn grand. On peut aussi voir que si le reste de la division de aa par bb est supérieur à b/2b/2, alors à l’étape suivante, le quotient vaudra 1, donc le reste suivant sera br<b/2b-r &lt; b/2, donc le nombre d’étapes d’Euclide classique est au plus 2 fois le nombre de bits de l’écriture en base 2 de bb.

Exercice : traduire l’algorithme d’Euclide en un programme non récursif.
Solution avec reste symétrique, cf. A.2

Le même algorithme fonctionne pour les polynômes, cette fois on cherche un polynôme de degré le plus grand possible qui divise AA et BB. L’algorithme ci-dessus s’adapte en remplaçant la division euclidienne des entiers par celle des polynômes, et le nombre d’appels récursifs est fini car le degré du reste qui diminue de 1 au moins à chaque itération. On obtient alors un PGCD, les autres PGCD sont des multiples non nuls de ce PGCD.

Il existe une version adaptée à l’écriture en base 2 des entiers sur machine, appelée PGCD binaire, qui évite de devoir faire des divisions euclidiennes (les divisions par 2 sont des décalages)

Chaque itération nécessite au plus le nombre nn de bits de l’écriture en base 2 de aa et bb, et il y a au plus 2n2n itérations (car on diminue de 1 le nombre de bits d’au moins un des 2 opérandes). Cet algorithme a donc un coût d’au plus O(n 2)O(n^2), mais la constante devant n 2n^2 est significativement plus petite que celle de l’algorithme d’Euclide car on évite les divisions qui sont des opérations arithmétiques de base couteuses. Sa justification repose sur des propriétés du PGCD qui se déduisent eux-mêmes de l’identité de Bézout.

3.5  L’identité de Bachet-Bézout

On va construire deux entiers relatifs uu et vv tels que au+bv=d=pgcd(a,b)au+bv=d = \mbox{pgcd}(a,b) On considère la matrice (L 1 1 0 a L 2 0 1 b)\left(\begin{array}{rccc} L_1 & 1 & 0 & a \\ L_2 & 0 & 1 & b \end{array}\right) qu’il faut interpréter ligne par ligne comme a×a\times1er coefficient+b×+b\times 2ème coefficient=3ième coefficient. L’algorithme de construction de uu et vv est une version arithmétique de l’algorithme du pivot de Gauss, où on souhaite créer un zéro dans la dernière colonne de la matrice. Mais seules les manipulations de lignes du type L n+2=L nqL n+1L_{n+2}=L_n-qL_{n+1} avec qq entier sont admises. Le mieux que l’on puisse faire est donc de créer L 3=L 1qL 2L_3=L_1-qL_2 avec qq quotient euclidien de aa par bb. Il faudra donc plusieurs manipulations de lignes avant d’avoir un 0, et la dernière colonne sera la liste des restes successsifs de l’algorithme d’Euclide pour calculer le PGCD de aa et bb. La ligne précédent la ligne avec un 0 en dernière colonne se termine par le PGCD (dernier reste non nul) et les deux premières colonnes donnent les coefficients de Bézout. Exemple avec a=125a=125 et b=45b=45 (L 1 1 0 125 L 2 0 1 45 L 3=L 12L 2 1 2 35 L 4=L 2L 3 1 3 10 L 5=L 34L 4 4 11 5 L 6=L 42L 5 9 25 0 )\left(\begin{array}{rccc} L_1 & 1 & 0 & 125 \\ L_2 & 0 & 1 & 45 \\ L_3=L_1-2L_2 & 1 & -2 & 35\\ L_4=L_2-L_3 & -1 & 3 & 10 \\ L_5=L_3-4L_4 & 4 & -11 & 5 \\ L_6=L_4-2L_5 & -9 & 25 & 0 \\ \end{array}\right)

Algorithme :

Exemple : exécutez la commande ci-dessous qui calcule l’identité de Bézout pour 125 et 45 en affichant les étapes de l’algorithme dans la partie basse de la page HTML

Explication : A chaque itération, l’élément l[2] d’indice 2 de la liste l 1l_1 (ou l 2l_2) prend comme valeur l’un des restes successifs de l’algorithme d’Euclide, et on a un invariant de boucle a*l[0]+b*l[1]=l[2] qui donne l’identité de Bézout lorsque l[2] est le dernier reste non nul. Cet invariant se prouve facilement par récurrence.

Regardons maintenant la taille de uu et de vv. On peut supposer que aba \geq b quitte à échanger aa et bb et on ignore les cas triviaux où aa et/ou bb valent 1.

Proposition 3   Si ab2a \geq b \geq 2, alors |u|b|u| \leq b et |v|a|v| \leq a.

Preuve (abrégée) : On considére les trois suites u nu_n (coefficients de la colonne d’indice 0), v nv_n (colonne d’indice 1) et r nr_n (colonne d’indice 2), ces suites vérifient la même relation de récurrence : u n+2=u nq nu n+1,v n+2=v nq nv n+1,r n+2=r nq nr n+1u_{n+2}=u_n-q_nu_{n+1}, \quad v_{n+2}=v_n-q_nv_{n+1}, \quad r_{n+2}=r_n-q_nr_{n+1} q nq_n est le quotient de la division de r nr_n par r n+1r_{n+1}. On montre par récurrence v nr n+1v n+1r n=(1) n+1a,(1) n+1v ncroissante (stricte pourn2)v_n r_{n+1}-v_{n+1} r_n=(-1)^{n+1} a, \quad (-1)^{n+1}v_n \ \mbox{croissante (stricte pour}\ n\geq 2 ) au cran après Bézout, on a |v N+1|pgcd(a,b)=a|v_{N+1}| \mbox{pgcd}(a,b)=a donc la suite |v n||v_n| est majorée par aa et |v|a|v| \leq a On en déduit |u|b|u| \leq b puisque u=(1bv)/au=(1-bv)/a.

On montre que le cout de l’algorithme est proportionnel à n 2n^2, comme pour l’algorithme d’Euclide.

Cet algorithme fonctionne à l’identique avec les polynômes à coefficients dans un corps, en utilisant la division euclidienne des polynômes.

L’identité de Bézout a de nombreuses conséquences en arithmétique. En voici une première :

Proposition 4   Tout diviseur commun à aa et bb est un diviseur du PGCD de aa et bb.

En effet, si cc divise aa et bb, il divise au+bvau+bv le PGCD de aa et bb.

3.6  Nombres premiers entre eux

Deux entiers sont dits premiers entre eux si leur PGCD vaut 1. Par exemple 22 et 15 sont premiers entre eux. Plus généralement, si aa et bb ont comme PGCD dd, alors a/da/d et b/db/d sont premiers entre eux, car leur PGCD multiplié par dd divise aa et bb.

Théorème 5   (Lemme de Gauss) :
Si
aa et bb sont premiers entre eux et si aa divise bcbc alors aa divise cc.

Preuve : par hypothèse, il existe un entier qq tel que bc=qabc=qa et par Bézout, deux entiers u,vu,v tels que au+bv=1au+bv=1, on élimine bb entre ces deux équations ce qui donne qav=bcv=bvc=(1au)cqav=bcv=bvc=(1-au)c er c=a(qv+uc)c=a(qv+uc) est bien multiple de aa.

Proposition 6   Si aa et bb sont premiers entre eux et divisent tous deux cc alors abab divise cc.

En effet, il existe des entiers q,q,u,vq,q',u,v tels que c=qa,c=qb,au+bv=1c=qa, \quad c=q'b, \quad au+bv=1 Donc c=c(au+bv)=acu+bvc=aqbu+bvqa=ab(qu+qv)c=c(au+bv)=acu+bvc=aq'bu+bvqa=ab(q'u+qv)

Ces résultats restent vrais avec des polynômes à coefficients dans un corps (où premiers entre eux signifie que le PGCD est constant).

Application : On en tire une méthode de résolution de l’équation ax+by=cax+by=c d’inconnues xx et yy. Soit dd le PGCD de aa et bb. Alors ax+byax+by est divisible par dd donc l’équation n’a pas de solution si cc n’est pas divisible par dd. Si cc est divisible par dd, alors une solution particulière est x=ucd,y=vcdx=u\frac{c}{d}, y=v\frac{c}{d}. Les autres solutions vérifient ax+by=c=ax+bya(xx)=b(yy),a=ad,b=bdax+by=c=ax'+by' \ \Rightarrow \ a'(x-x')=b'(y'-y), \quad a'=\frac{a}{d}, b'=\frac{b}{d} Comme aa' et bb' sont premiers entre eux, aa' divise yyy'-y donc y=y+qa,x=xqb,qy'=y+qa', x'=x-qb', \quad q \in \mathbb{Z}

Application : décomposition d’une fraction nab\frac{n}{ab} avec aa et bb premiers entre eux (entiers ou polynômes).
Il existe alors uu et vv tels que au+bv=1au+bv=1, donc UU et VV tels que n=aU+bVn=aU+bV et nab=aU+bVab=aUab+bVab=Ub+Va\frac{n}{ab} = \frac{aU+bV}{ab}=\frac{aU}{ab}+\frac{bV}{ab}=\frac{U}{b}+\frac{V}{a} Exemple 1 : 23×5\frac{2}{3\times 5}, on a 1=2×31×51=2\times 3-1 \times 5 donc 2=4×32×52=4\times 3-2 \times 5 et 215=4×32×53×5=4523\frac{2}{15} = \frac{4\times 3-2 \times 5}{3 \times 5}=\frac{4}{5}-\frac{2}{3}

Exemple 2 : 2x 21=2(x1)(x+1)\frac{2}{x^2-1}=\frac{2}{(x-1)(x+1)} Les polynômes x1x-1 et x+1x+1 sont premiers entre eux, on a l’identité de Bézout x1(x+1)=2x-1 - (x+1) = -2 donc 2(x1)(x+1)=(x+1)(x1)(x1)(x+1)=1x11x+1\frac{2}{(x-1)(x+1)} = \frac{(x+1)-(x-1)}{(x-1)(x+1)} = \frac{1}{x-1} - \frac{1}{x+1} Ce qui permet de calculer une primitive de 2x 21\frac{2}{x^2-1}. 2x 21=ln(|x1|)ln(|x+1|)\int \frac{2}{x^2-1}= \ln(|x-1|)-\ln(|x+1|)

3.7  Les restes chinois

L’identité de Bézout a une application très importante en calcul formel, elle permet de reconstruire un entier dont on connait le reste de la division par des entiers premiers entre eux :

Théorème 7   (restes chinois)
Soit
mm et nn deux entiers naturels premiers entre eux, alors pour tout aa et bb entiers relatifs, il existe un entier cc tel que cac-a est divisible par mm et cbc-b est divisible par nn.

Il existe une infinité de solutions, deux solutions cc et cc' distinctes diffèrent par un multiple de nmnm. Il y a donc existence et unicité dans n’importe quel intervalle contenant nmnm entiers.

Remarque : lorsqu’on reconstruit un entier naturel plus petit que nmnm, on choisit l’unique valeur de cc telle que c[0,nm[c \in [0,nm[ (c’est le reste de la division par nmnm de n’importe quelle solution), lorsqu’on reconstruit un entier relatif de valeur absolue plus petite que nm/2nm/2, on choisit cc tel que |c|nm/2|c|\leq nm/2 (on prend le reste symétrique).

Preuve : Si on a deux solutions cc et cc' alors ccc-c' est divisible par nn et mm qui sont premiers entre eux, donc ccc-c' est divisible par nmnm. Cherchons une solution, cela revient à chercher UU et VV tels que ca=mU,cb=nVc-a=mU, \quad c-b= nV On a donc a+mU=b+nVmU+nV=baa+mU=b+nV \quad \Leftrightarrow \quad -mU+nV=b-a Or mm et nn sont premiers entre eux, donc par Bézout, il existe uu et vv tels que mu+nv=1mu+nv=1 il suffit de multiplier par bab-a pour trouver un couple de valeurs U,VU,V qui convient.

On sait que le couple U,VU,V n’est pas unique, si U,VU',V' est une autre solution, alors ba=mU+nV=mU+nVb-a=-mU+nV=-mU'+nV' donc n(VV)=m(UU)n(V-V')=m(U-U') donc nn divise UUU-U' et mm divise VVV-V' avec le même quotient. Pour déterminer efficacement une valeur de cc qui convient, on calculera vv tel que |v|m|v|\leq m par l’algorithme de Bézout, on multipliera par bab-a et on prendra pour VV le reste de V(ba)V(b-a) par mm.

Exemple : trouver c[0,13×7=91[c \in [0,13\times 7=91[ tel que le reste de cc par 13 est 11 et le reste de cc par 7 est 3. On veut c=11+13U=3+7Vc=11+13U=3+7V donc 13U7V=813U-7V=-8 On a l’identité de Bézout : (1)×13+7×2=1(-1)\times 13 + 7\times2 = 1 on multiplie par -8 donc on peut prendre V=16V=16 (U=8U=8), on préfère prendre le reste par 13 donc V=3V=3, c=24c=24 (et U=1U=1).

Remarque : en calcul formel, on prend m=p 1...p k1m=p_1...p_{k-1} un produit de nombres premiers distincts, et n=p kn=p_{k} un nombre premier distinct des précédents, on calcule la valeur de cc modulo chaque nombre premier p jp_j et dès qu’on a assez de nombres premiers p kp_k (i.e. si |c|<p 1...p k/2|c|&lt;p_1...p_k/2) on en déduit cc. On parle d’algorithme modulaire. Par exemple, on peut calculer le déterminant d’une matrice à coefficients entiers de cette manière.

Cette méthode s’applique aussi aux polynômes, mais elle est moins utile, car l’interpolation polynomiale de Lagrange est plus efficace lorsque les nombres premiers p ip_i sont remplacés par des xα ix-\alpha_i

3.8  Nombres premiers, factorisation

Définition 8   Un entier naturel est premier s’il est divisible seulement par deux entiers distincts: 1 et lui-même.

Exemple : 17 est premier, mais pas 18. 1 n’est pas premier.

Un nombre non premier admet un diviseur différent de 1 et lui-même donc une factorisation n=pqn=pq avec p1p \neq 1 et q1q \neq 1. On peut supposer que pqp \leq q, pour savoir si un nombre est premier on peut donc tester que tous les entiers plus petits ou égaux à n\sqrt{n} ne divisent pas nn.

On a la

Proposition 9   Si pp premier ne divise pas un entier aa, alors aa et pp sont premiers entre eux. En particulier si pp et qq sont deux nombres premiers distincts, alors ils sont premiers entre eux.
Si
pp premier divise abab alors pp divise aa ou pp divise bb.

Si pp ne divise pas aa, alors la liste des diviseurs de aa ne contient pas pp, donc la liste des diviseurs communs à aa et pp est réduite à 1 donc aa et pp sont premiers entre eux.
Si pp divise abab, soit pp divise aa, soit pp est premier avec aa donc par le lemme de Gauss il divise bb.

On en déduit :

Proposition 10   Si q 1,..,q kq_1,..,q_k sont des nombres premiers tous distincts de pp premier, alors pp et a=q 1...q ka=q_1...q_k sont premiers entre eux.

Sinon, pp divise a=q 1...q na=q_1...q_n donc pp divise l’un des facteurs q iq_i donc est égal à q iq_i puisque q iq_i est premier.

Théorème 11   Un entier naturel n>1n&gt;1 s’écrit de manière unique (à permutation près) comme produit de nombres premiers. n= ip i m in = \prod_{i} p_i^{m_i} L’exposant m im_i est la multiplicité de p ip_i.

Preuve de l’existence par récurrence. Pour n=2n=2, on a 2=22=2. Pour n>2n&gt;2, si nn est premier, alors n=nn=n, sinon il admet un plus petit diviseur strictement supérieur à 1, on a n=pqn=pq avec p<np&lt;n premier et q<nq&lt;n à qui on applique l’hypothèse de récurrence.

Exercice : traduire cette preuve en un algorithme.

Unicité: supposons p 1...p k=p 1...p lp_1...p_k=p'_1...p'_l. Donc p jp_j divise p 1...p lp'_1...p'_l. Comme p jp_j est premier, il doit être égal à un des p lp'_l. On divise par ce facteur commun et on recommence.

Remarques :

Des résultats analogues s’appliquent pour les polynômes à coefficients dans un corps: on parle de facteur irréductible (analogue de nombre premier), i.e. polynome divisible seulement par des polynomes constants et des polynomes multiples non nuls d’eux-mêmes. Par exemple tous les polynômes de degré 1 sont irréductibles. Un polynôme s’écrit de manière unique à permutation près et à inversibles près comme produit de facteurs irréductibles.

3.9  Prolongement : idéaux

Un idéal est un sous-ensemble d’un anneau qui est stable pour l’addition (la somme de deux éléments de l’idéal est encore dans l’idéal), et stable par multiplication par tout élément de l’anneau. Par exemple si on veut résoudre un système de plusieurs équations en une inconnue, l’ensemble des polynômes en une variable qui s’annulent en un point est un idéal.

Dans les anneaux euclidiens, tous les idéaux sont principaux, i.e. engendrés par un élément, on prend un plus petit entier en valeur absolue pour les entiers ou un plus petit polynôme en degré pour les polynômes, la division euclidienne par cet élément doit renvoyer 0 comme reste sinon on aurait un élément dans l’idéal plus petit. Dans l’exemple ci-dessus, on peut se ramener à une équation polynomiale en une variable (c’est un PGCD des polynômes des équations).

Dans les anneaux non euclidiens, comme par exemple les polynômes à plusieurs variables, les idéaux ne sont pas forcément engendrés par un seul élément, ce qui rend la résolution de systèmes polynomiaux à plusieurs inconnues beaucoup plus délicate qu’en une inconnue. Il est en général difficile de vérifier qu’un élément de l’anneau appartient ou non à l’idéal (alors que c’est juste une division dans le cas à une variable). Heureusement, il existe des familles particulières qui engendrent l’idéal pour lesquelles la vérification se fait par une généralisation de la division euclidienne. La recherche efficace de telles familles, appelées bases de Groebner, est un domaine de recherche actif!

4  L’anneau /n\mathbb{Z}/n\mathbb{Z} et le corps /p\mathbb{Z}/p\mathbb{Z}

4.1  Définition

4.1.1  Congruences

On dit que deux entiers aa et bb sont congrus modulo nn si aba-b est divisible par nn. On vérifie immédiatement les propriétés suivantes qui caractérisent une relation d’équivalence :

L’ensemble des entiers congrus à aa modulo nn est appelé classe de aa modulo nn et est notée a¯\overline{a}. Par exemple la classe de 3 modulo 11, 3¯={...,19,8,3,14,25,...}\overline{3}=\{ ...,-19,-8,3,14,25,...\} Les propriétés ci-dessus montrent que a¯=b¯\overline{a}=\overline{b} si et seulement si aa et bb sont congrus modulo nn (ou encore si et seulement si ba¯b \in \overline{a}). Un élément de de a¯\overline{a} est appelé représentant de la classe. Deux classes distinctes ont une intersection vide.

On peut définir les opérations de somme et de produit sur les classes, car ces opérations sont compatibles avec la divisibilié par nn, ainsi a¯+b¯=a+b¯,a¯b¯=ab¯\overline{a}+\overline{b}=\overline{a+b}, \overline{a} \overline{b}=\overline{ab} et le résultat ne dépend du choix du représentant dans la classe de aa et de bb. En effet si aa' et bb' sont deux autres représentants, on a a=a+αna'=a+\alpha n et b=b+βnb'=b+\beta n donc a+b=a+b+(α+β)n,ab=ab+(αb+βa+αβ)na'+b'=a+b+(\alpha+\beta)n, \quad a'b'= ab+(\alpha b+ \beta a + \alpha \beta)n

Comme il y a nn reste possibles lorsqu’on divise un entier par nn, il y a nn classes distinctes, l’ensemble de ces classes est appelé /n\mathbb{Z}/n\mathbb{Z} et on vient de voir qu’il possède une structure d’anneau. Nous allons dans la suite de cette section nous intéresser aux propriétés de la multiplication dans cet anneau.

Optimisation : lorsqu’on travaille modulo un entier fixé, on peut optimiser les divisions euclidiennes par cet entier en utilisant un algorithme dû à Granlund et Montgomery. Le lecteur intéressé pourra rechercher des informations sur le site d’Agner Fog (volume 2, section 16.9).

4.1.2  Généralisation: relations d’équivalence, classes d’équivalence

On peut faire la même chose dans tout ensemble EE possédant une relation d’équivalence, i.e. une relation binaire \mathcal{R} vérifiant les propriétés de réflexivité, symétrie et transitivité, on obtient ainsi que EE est réunion disjointe de classes d’équivalence E/E/\mathcal{R}. Si EE est muni d’une loi de groupe + compatible avec la relation d’équivalence (i.e. aaa \mathcal{R} a' et bbb \mathcal{R} b' entraine que (a+b)(a+b)(a+b) \mathcal{R} (a'+b')), alors l’ensemble des classes d’équivalence E/E/\mathcal{R} muni de la loi + est un groupe. De même on obtient un anneau si on a compatibilité avec les deux lois + et ×\times d’un anneau EE.

Par exemple, sur les polynômes à coefficients dans un corps KK, étant donné un polynôme MM on peut définir la congruence modulo MM, i.e. ABA \mathcal{R} B si le reste de la division euclidienne de ABA-B par MM est nul. On vérifie que cette relation est une relation d’équivalence, compatible avec l’addition et la multiplication des polynômes.

Autre exemple: si on a un anneau commutatif unitaire intègre, on peut construire son corps des fractions : il est constitué de couples (n,d)(n,d), deux couples sont équivalents si nd=ndnd'=n'd. Si on a un anneau euclidien, on peut alors construire un représentant privilégié qui est une fraction n/dn/d telle que nn et dd soient premiers entre eux. En ajoutant des conditions pour tenir compte des inversibles de l’anneau, on peut construire un représentant unique. Ainsi pour le corps des fractions de \mathbb{Z}, nn et dd sont premiers entre eux et d>0d&gt;0. Pour celui des polynômes à coefficients rationnels, on peut montrer qu’on a un représentant sous forme de fraction de deux polynômes à coefficients entiers primitifs (le PGCD des coefficients vaut 1) de coefficient dominant strictement positif multiplié par une fraction irréductible d’entiers.

4.2  Le groupe des inversibles

L’ensemble des éléments inversibles pour la multiplication d’un anneau forme un groupe multiplicatif. En effet le produit de deux inversibles est encore inversible (ab) 1=b 1a 1(ab)^{-1}=b^{-1} a^{-1}, le neutre de la multiplication est inversible (son inverse est lui-même), et tout élément admet un symétrique pour le produit puisqu’il est supposé inversible.
Exemple : les inversibles modulo 8 sont 1, 3, 5 et 7. Pour obtenir la table de multiplication du groupe, on peut valider la commande


On vérifie bien que chaque ligne contient un 1, donc chaque élément est inversible.

Dans un groupe (avec une loi notée multiplicativement), on a le

Théorème 1   (Lagrange)
Soit
GG un groupe ayant un nombre fini gg d’éléments, alors pour tout xGx \in G, x g=x×...×x=1 Gx^g=x\times ... \times x=1_G l’élément neutre du groupe (dans le produit avec les ..., il y a gg occurences de xx et g1g-1 signes ×\times).
De plus le plus petit entier
k>0k&gt;0 tel que x k=1 Gx^k=1_G, appelé ordre de l’élement xx, divise gg.

Exemple : pour les inversibles modulo 8, on a 1 1(mod8)=1,3 2=1(mod8),5 2=1(mod8),7 2=1(mod8)1^1 \pmod 8 =1, 3^2=1 \pmod 8, 5^2=1 \pmod 8, 7^2=1 \pmod 8

Preuve dans le cas où GG est commutatif.
Soit xGx \in G fixé. L’application de GG dans GG qui à yGy \in G associe xyxy est une bijection (de réciproque yx 1yy \rightarrow x^{-1}y). Donc le produit de tous les éléments de GG est égal au produit de leurs images. Comme le groupe est supposé commutatif, c’est le produit des images multiplié par x gx^g, on en déduit que x g=1 Gx^g=1_G.
Montrons que kk divise gg : soit rr le reste de la division de gg par kk, on a g=kq+rg=kq+r donc x g=(x k) qx rx^g=(x^k)^q x^r donc x r=1 Gx^r=1_G donc r=0r=0 car r<kr&lt;k et kk est le plus petit entier non nul tel que x k=1 Gx^k=1_G.

Remarque : ce résultat est encore vrai si GG n’est pas commutatif, avec une preuve différente (cf. par exemple wikipedia).

Revenons aux entiers et aux entiers modulo nn. Soit donc n>1n &gt;1 un entier. Un entier aa est inversible modulo nn (ou une classe de congruence a¯\overline{a} est inversible dans /n\mathbb{Z}/n\mathbb{Z}) si et seulement si il existe un entier uu tel que au1au-1 est divisible par nn, donc si et seulement si il existe deux entiers uu et vv tels que au1=vnau-1=vn, i.e. auvn=1au-vn=1. On retrouve l’identité de Bézout. Les inversibles de /n\mathbb{Z}/n\mathbb{Z} sont les classes dont les représentants sont premiers avec nn et le calcul de l’inverse d’un entier aa modulo nn est obtenu par le coefficient de aa dans l’identité de Bézout pour aa et nn.

Définition 2   On appelle indicatrice d’Euler le nombre d’entiers m[0,n[m \in [0,n[ qui sont premiers avec nn, on le note φ(n)\varphi(n).

C’est le nombre d’éléments inversibles de /n\mathbb{Z}/n\mathbb{Z}. Par exemple si n=pn=p est premier, φ(p)=p1\varphi(p)=p-1. Autre exemple, si n=15n=15, les inversibles sont {1,2,4,7,8,11,13,14}\{ 1, 2, 4, 7, 8, 11,13, 14\} et φ(15)=8\varphi(15)=8.

Le théorème de Lagrange donne alors :

Corollaire 3   si aest premier avecn,a φ(n)=1(modn)\mbox{si } \ a \ \mbox{est premier avec} \ n, \quad a^{\varphi(n)}=1 \pmod n

Exemple : 2 8=16 2=(161)×(16+1)+1=1(mod15) 2^8=16^2=(16-1)\times (16+1)+1=1 \pmod 15.
Remarque :On retrouve des résultats de la section motivation, si n=pn=p est premier et a[1,p[a \in [1,p[, alors a p1=1(modp)a^{p-1}=1 \pmod p.

Calcul de φ(n)\varphi(n) connaissant la factorisation de nn

Exemple : φ(15)=φ(3×5)=φ(3)φ(5)=2×4=8\varphi(15)=\varphi(3 \times 5)=\varphi(3) \varphi(5)=2 \times 4=8 Si n=pqn=pq est le produit de deux nombres premiers distincts, on a φ(pq)=(p1)(q1)\varphi(pq)=(p-1)(q-1) Plus généralement, on a pour tout aa premier avec n=pqn=pq produit de deux nombres premiers distincts : a (p1)(q1)=1(modn)a^{(p-1)(q-1)} = 1 \pmod n résultat déjà obtenu dans la section motivations.

Remarques :

4.3  L’algorithme d’exponentiation rapide

Il s’agit de calculer efficacement a k(modn)a^k \pmod n. On a vu dans la section motivation un algorithme récursif qui permet de le faire efficacement. On commence par remplacer aa par a(modn)a \pmod n puis

Il y a au plus ceil(2log 2(k)2 \log_2(k) ) multiplications, qui font intervenir des entiers plus petits que nn donc le coût est en O(log 2(k)log 2(n) 2)O(\log_2(k) \log_2(n)^2) (avec multiplication naive pour les entiers).

Il existe une version itérative de l’exponentiation rapide, utilisant la décomposition en base 2 de l’exposant que l’on calcule simultanément.
On initialise AaA \leftarrow a, b1b \leftarrow 1, puis tant que k0k \neq 0

et on renvoie bb. En effet après ii itérations, AA vaut a 2 i(modn)a^{2^i} \pmod n et a k=a ik i2 i= i/k i=1a 2 ia^k = a^{\sum_i k_i 2^i} =\prod_{i/k_i=1} a^{2^i}

4.4  Le corps /p\mathbb{Z}/p\mathbb{Z}

Lorsque n=pn=p est premier, tous les éléments de l’anneau /p\mathbb{Z}/p\mathbb{Z} sauf 0¯\overline{0} sont inversibles, on est alors en présence d’un corps ayant pp éléments, et dont le groupe multiplicatif des inversibles possède p1p-1 éléments.

Une propriété très importante des corps est que le nombre de racines d’un polynôme de degré dd est au plus dd. Cela résulte du fait qu’il n’y a pas de diviseurs de 0 dans un corps, donc si rr est racine de PP, alors on peut écrire P=(Xr)QP=(X-r)QQQ est de degré d1d-1, les autres racines de PP sont exactement les racines de QQ. Cette propriété n’est pas vraie lorsqu’on considère un polynôme à coefficients dans un anneau qui n’est pas un corps, par exemple x 2=4(mod15)x^2=4 \pmod 15 admet 4 solutions 2, 7, 8, 13.

On rappelle que par le théorème de Lagrange, x p1=1(modp)x^{p-1}=1 \pmod p pour xx inversible modulo pp. On a donc X p11= i=1 p1(Xi)(modp)X^{p-1}-1=\prod_{i=1}^{p-1}(X-i) \pmod p car ces deux polynômes ont les mêmes p1p-1 racines distinctes et même coefficient dominant.

On peut alors se demander si l’ensemble des puissances de xx est égal à /p\mathbb{Z}/p\mathbb{Z}. On peut se poser la même question sur \mathbb{C} pour les racines complexes de z p1=1z^{p-1}=1 qui sont de la forme e 2ikπp1,k[0,p2]e^{\frac{2ik\pi}{p-1}}, \quad k \in [0,p-2] la réponse sur \mathbb{C} est que c’est le cas si kk est premier avec p1p-1, par exemple k=1k=1. Ici, on a le

Théorème 4   Il existe un générateur des inversibles de /p\mathbb{Z}/p\mathbb{Z}, i.e. un élément xx tel que /p={0,1,x,x 2,...,x p2}\mathbb{Z}/p\mathbb{Z} = \{ 0, 1, x ,x^2,...,x^{p-2} \}

xx est appelé racine primitive p1p-1-ième de 1 (modulo pp), parce que x p1=1x^{p-1}=1, mais x k1x^k \neq 1 pour k<p1k&lt;p-1. Les autres générateurs des inversibles sont obtenus en prenant x kx^k pour kk premier avec p1p-1, il y en a donc φ(p1)\varphi(p-1).

On donne la preuve de ce théorème pour être complet, étant entendu qu’elle se situe au-delà du niveau de ce cours. On factorise p1= ip i m ip-1=\prod_i p_i^{m_i} en produit de facteurs premiers, les p ip_i étant deux à deux distincts. Montrons que pour tout ii, il existe un élément x ix_i de /p *\mathbb{Z}/p\mathbb{Z}^* d’ordre p i m ip_i^{m_i}. En effet, le polynôme P(X)=X p i m i1 jip j m j1P(X)=X^{ p_i^{m_i-1}\prod_{j \neq i} p_j^{m_j} } -1 n’est pas identiquement nul sur /p *\mathbb{Z}/p\mathbb{Z}^* car son degré est strictement inférieur à p1p-1. Soit a0a \neq 0 non racine de PP, on prend x i=a jip j m jx_i=a^{\prod_{j \neq i} p_j^{m_j} }, on a x i p i m i1=P(a)+11x_i^{p_i^{m_i-1}}=P(a)+1 \neq 1 et x i p i m i=a p1=1x_i^{p_i^{m_i}}=a^{p-1}=1. On pose alors x= ix ix=\prod_i x_i. On vérifie que xx est bien d’ordre p1p-1 avec le critère ci-dessous.

Critère : Pour savoir si xx est d’ordre p1p-1, comme l’ordre d’un élément divise p1p-1, si on connait les facteurs premiers p ip_i de p1= ip i m ip-1=\prod_i p_i^{m_i}, il suffit de vérifier que x p1p i1pourp ifacteur premier dep1x^{\frac{p-1}{p_i}} \neq 1 \quad \mbox{pour} \ p_i \ \mbox{facteur premier de}\ p-1 Pour trouver un générateur de /p *\mathbb{Z}/p\mathbb{Z}^*, on teste alors si x=2,3,...x=2, 3, ... convient en utilisant l’algorithme d’exponentiation rapide. La probabilité de trouver un générateur en tirant un entier au hasard dans [1,p[[1,p[ vaut : φ(p1)p1= i(p i m ip i m i1) ip i m i= i(11p i)\frac{\varphi(p-1)}{p-1} = \frac{\prod_i (p_i^{m_i}-p_i^{m_i-1})}{\prod_i p_i^{m_i}} = \prod_i(1-\frac{1}{p_i})

Remarque : la recherche de racines primitives est un ingrédient essentiel pour faire fonctionner la transformée de Fourier rapide exacte, plus précisément on cherche des racines primitives 2 k2^k-ième de 1 modulo pp avec pp premier de la forme p=1+2 kmp=1+2^km.

4.5  L’équation ax=b(modp)ax=b \pmod p

Si pp est premier, l’équation est du premier degré dans un corps, donc

Prolongement: ax=b(modn)ax=b \pmod n
On peut faire le même raisonnement que ci-dessus lorsque aa est inversible modulo nn, on a alors une solution unique. Sinon, on écrit l’équation sous la forme AX+nY=BAX+nY=B AA est un représentant de la classe de aa et nn, BB de bb. Si le pgcd de AA et nn (qui est indépendant du représentant choisi) vaut d>1d&gt;1, il faut que BB soit divisible par dd pour qu’il y ait des solutions (ceci ne dépend pas des représentants choisis dans la classe déquivalence), que l’on sait alors calculer en partant de l’identité de Bézout pour A/dA/d et n/dn/d. Il y a alors dd solutions modulo nn, en effet pour k[0,d1]k \in [0,d-1] on a : a(x+knd)=b(modn)a(x+k\frac{n}{d})=b \pmod n

Si on connait la factorisation de nn, on peut un peu gagner en efficacité sur les calculs intermédiaires.

4.6  Prolongement : l’équation x 2=a(modp)x^2=a \pmod p

Si a=0a=0, il y a une solution unique x=0x=0. Si a0a\neq 0, si l’équation a une solution, alors a (p1)/2=x p1=1(modp)a^{(p-1)/2}=x^{p-1}=1 \pmod p. Réciproquement, on si a (p1)/2=1(modp)a^{(p-1)/2}=1 \pmod p alors on montre qu’il y a bien 2 solutions opposées, en comptant les carrés non nuls (il y en a (p1)/2(p-1)/2 qui est le degré de X (p1)/21X^{(p-1)/2}-1, ils forment donc l’ensemble des solutions de X (p1)/2=1(modp)X^{(p-1)/2}=1 \pmod p).

Exemple 1 : p=7,a=2p=7, a=2. On a a (p1)/2=2 3=8=1(mod7)a^{(p-1)/2}=2^3=8=1 \pmod 7 donc 2 est un carré modulo 7, et les racines carrées sont b=±a (p+1)/4=±2 2=±4(mod7)=3(mod7)b=\pm a^{(p+1)/4}=\pm 2^2=\pm 4 \pmod 7=\mp 3 \pmod 7
Exemple 2 : p=5,a=2p=5, a=2. On a a (p1)/2=2 2=4=1(mod5)a^{(p-1)/2}=2^2=4=-1 \pmod 5 donc 2 n’est pas un carré modulo 5.
Exemple 3 : p=5,a=4p=5, a=4. On a a (p1)/2=4 2=16=1(mod5)a^{(p-1)/2}=4^2=16=1 \pmod 5 donc 4 est un carré modulo 5. Comme 4 est un carré parfait, les deux racines carrées sont 2 et -2. Voici les PGCD de (X+r) 21(X+r)^2-1 et X 24X^2-4 modulo 5 :
Vous pouvez essayer d’autres valeurs de pp premier pour vérifier que tirer au hasard rr donne un PGCD de degré 1 environ une fois sur 2.

On peut alors résoudre les équations du second degré si p2p\neq 2 avec la formule habituelle du discriminant.

Remarque
Comme pour l’équation du premier degré, on peut résoudre x 2=a(modn)x^2=a \pmod n lorsque nn n’est pas premier. On commence par le cas d’une puissance de nombre premier, où on utilise un algorithme pp-adique comme dans la section précédente. Le cas général il se traite avec le théorème des restes chinois. Ainsi pour n=pqn=pq produit de deux premiers distincts, on a 4 racines carrées en cas d’existence. Par exemple, 4 admet 2, 7, 8, 13 comme racines carrées modulo 15.

4.7  Prolongement: 𝔽 p[X]=/p[X]\mathbb{F}_p[X]=\mathbb{Z}/p\mathbb{Z}[X]

L’anneau des polynômes 𝔽 p[X]\mathbb{F}_p[X] à coefficients dans /p[X]\mathbb{Z}/p\mathbb{Z}[X] est un anneau euclidien, on peut en effet effectuer des divisions euclidiennes comme sur [X]\mathbb{Q}[X]. On peut donc définir le PGCD de 2 polynômes non simultanément nuls de 𝔽 p[X]\mathbb{F}_p[X] (normalisé par coefficient dominant 1), et les calculs sont beaucoup plus simples que sur \mathbb{Q}, et c’est l’ingrédient principal des algorithmes efficaces de calcul de PGCD sur [X]\mathbb{Q}[X].

4.8  Test de primalité

Test de Fermat : Si pp est premier et aa premier avec pp, alors a p1=1(modp)a^{p-1}=1 \pmod p. Donc si on trouve un entier aa premier avec nn tel que a n11(modn)a^{n-1} \neq 1 \pmod n alors nn n’est pas premier.
Exemple : a=2a=2 et n=1345677n=1345677

Il est important de voir qu’on a alors prouvé que nn n’est pas premier sans déterminer un facteur non trivial de nn. Le temps de calcul d’un test de Fermat est proportionnel à ln(n) 3\ln(n)^3, alors que la factorisation naïve de nn peut couter nln(n) 2\sqrt{n} \ln(n)^2.

Attention, si a n1=1(modn)a^{n-1}=1 \pmod n pour deux entiers aa et nn premiers entre eux, cela ne prouve pas que nn est premier! Il existe d’ailleurs des entiers non premiers pour lesquels cette propriété est vraie pour tout aa premier avec nn, ce sont les nombres de Carmichael, le plus petit est 561.

Exercice : Implémenter un programme déterminant tous les nombres de Carmichael plus petits que nn.

Prolongement : test de Miller-Rabin : c’est une version améliorée du test précédent. Il part de l’observation suivante : soit aa premier avec pp nombre premier impair, si a p1=1(modp)a^{p-1}=1 \pmod p alors a (p1)/2=±1(modp)a^{(p-1)/2}=\pm 1 \pmod p (1 a exactement deux racines carrées dans /p\mathbb{Z}/p\mathbb{Z}). Si a (p1)/2=1(modp)a^{(p-1)/2}=1 \pmod p et (p1)/2(p-1)/2 est pair, on peut recommencer le même raisonnement. Plus précisément, soit on trouve un -1, soit on ne peut plus diviser par 2 l’exposant. En raisonnant dans l’autre sens, si p1=2 tsp-1=2^t s avec ss impair, alors a s=±1(modp)a^s=\pm 1 \pmod p ou l’un des carrés successifs doit valoir -1 modulo pp (si on tombe sur un 1 le test échoue et le nombre n’est pas premier)

def millerrabin(a,n):
  t=0
  s=n-1
  while s % 2==0:
     t += 1
     s = s // 2
  a=pow(a,s,n)
  if a==1 or a==n-1: return True
  for j in range(t):  
    a=(a*a) % n
    if a==1: return False
    if a==n-1: return True
  return False

onload

Comme pour le test de Fermat, si le test renvoie faux, on est sur que le nombre n’est pas premier (aa est appelé témoin de Miller), s’il renvoie vrai on ne peut rien prouver. Mais on peut montrer que si nn est non premier, le nombre d’entiers a<na&lt;n qui renvoient True est au plus n/4n/4, ce qu’on peut plus ou moins traduire par si on tire kk entiers aa “au hasard” et que le test de Miller-Rabin renvoie vrai à chaque fois, il y a moins d’une chance sur 4 k4^k que nn ne soit pas premier. Par exemple pour k=20k=20 et en prenant les 20 premiers nombres premiers, 1/4 201/4^{20} est de l’ordre de 1e-12, la probabilité de considérer premier un nombre qui ne l’est pas est très faible. On parle de nombre pseudo-premier.

Remarques :

4.9  Application: cryptographie RSA

4.9.1  Interprétation du codage et du décodage.

Nous revenons sur la cryptographie RSA, en utilisant les notions vue dans les sections précédentes. On rappelle qu’on se donne un entier n=pqn=pq produit de deux nombres premiers pp et qq. Les entiers pp et qq sont secrets alors que nn est public. On choisit ensuite deux entiers dd et ee tels que de=1+k(p1)(q1)de=1 + k(p-1)(q-1), ou encore dd et ee sont inverses modulo l’indicatrice d’Euler de nn : de=1(modφ(n))de=1 \pmod {\varphi(n)} Le codage et le décodage se font avec l’algorithme de la puissance modulaire rapide : ab=a e(modn)c=b d(modn)a \rightarrow b=a^e \pmod n \rightarrow c=b^d \pmod n le cout est de O(ln(e)ln(n) 2)O(\ln(e)\ln(n)^2) et O(ln(d)ln(n) 2)O(\ln(d)\ln(n)^2) opérations.

On peut maintenant facilement montrer que ces deux fonctions sont réciproques l’une de l’autre lorsque aa est premier avec nn. En effet, on a a φ(n)=1(modn)a^{\varphi(n)}=1 \pmod n donc : c=a de(modn)=a 1+kφ(n)(modn)=a(a φ(n)) k(modn)=a(modn)c=a^{de} \pmod n = a^{1+k \varphi(n)} \pmod n =a (a^{\varphi(n)})^k \pmod n = a \pmod n

4.9.2  Comment générer des clefs

On choisit pp et qq en utilisant le test de Miller-Rabin. Par exemple

On choisit un couple de clefs privée-publique en utilisant l’identité de Bézout (ou inverse modulaire). Par exemple

Ici, on a pris E de sorte que l’exponentiation modulaire rapide à la puissance E nécessite peu d’opérations arithmétiques (17), comparé au calcul de la puissance d, ceci permet de faire l’opération “publique” plus rapidement (encodage ou vérification d’une signature) si le microprocesseur a peu de ressources (par exemple puce d’une carte bancaire).

On peut utiliser un programme tel que ssh-keygen pour générer des clefs de taille industrielle. Par exemple la commande suivante
ssh-keygen -t rsa -b 2048 -m PEM -f rsa2048.key
génère une clef 2048 bits stockée dans le fichier rsa2048.key, on peut afficher en clair les valeurs de n,p,qn,p,q avec la commande
openssl rsa -in rsa2048.key -text -inform PEM -noout

On obtient une sortie analogue à ce qui suit

Private-Key: (2048 bit)
modulus:
    00:a1:e8:58:f4:81:34:c7:0c:e6:4e:12:a3:22:f3:
    2b:49:c4:7a:e0:c0:b6:11:07:3f:ea:f3:23:eb:d2:
    3d:97:99:35:65:ce:42:d6:9a:e9:53:3e:60:a2:90:
    28:7a:bf:23:1f:84:b0:10:4a:15:8c:dd:e1:29:43:
    53:cb:74:22:4f:f6:ae:ec:62:35:1e:61:0b:66:2e:
    fe:21:41:c1:56:6d:68:70:51:8b:42:6a:db:ca:13:
    e1:06:1d:db:da:33:82:96:70:0c:df:d6:4f:51:c2:
    51:57:ae:ec:a8:6d:06:f3:a3:02:b7:5d:79:3e:58:
    1b:e2:a2:e6:d8:97:58:c9:7f:f6:81:8d:ab:1e:9b:
    22:0a:4f:77:41:49:e6:5c:71:03:ee:70:9a:60:22:
    28:76:c1:53:a7:99:06:2a:22:9c:0b:9d:68:cc:2f:
    36:5a:84:bd:b1:03:36:cc:be:07:4b:17:8c:85:84:
    ef:d5:1e:31:e0:82:32:0e:52:85:d0:8e:40:0d:a8:
    ac:c7:ff:c1:c0:e9:fe:6c:49:a5:c9:ff:cc:15:f1:
    2b:1c:c6:70:29:97:77:da:3e:7c:f7:3f:bf:6f:17:
    91:ee:b6:5b:90:d8:ba:49:07:96:1b:a7:a9:5e:37:
    8f:4c:be:de:c4:07:ef:12:e6:78:a0:4d:95:42:13:
    2e:3b
publicExponent: 65537 (0x10001)
privateExponent:
    69:0e:97:f2:07:88:d4:84:15:48:a1:a5:43:7f:60:
    1e:5c:a4:93:03:d8:df:d1:c1:72:d5:d4:00:28:0a:
    99:3c:eb:be:24:89:90:31:32:a7:36:39:84:22:60:
    71:cd:66:a0:03:fc:2e:85:b3:d8:14:fd:0e:46:46:
    b0:24:aa:43:12:c1:4c:57:29:3a:8e:23:d4:69:37:
    b3:22:b4:ae:3d:0d:e0:9b:b8:ee:1e:e2:81:0c:47:
    1e:2d:ef:c3:75:5b:0d:fc:a5:0d:f5:44:c0:bb:83:
    06:8f:55:b6:b0:10:2b:b5:21:85:13:dd:21:4c:10:
    c4:0d:8a:17:0e:95:a9:21:1b:91:17:86:07:de:74:
    d5:3f:05:21:e7:57:67:3b:32:de:68:cf:a5:9b:61:
    6f:f7:89:c3:db:a4:75:53:71:b8:77:ab:b5:22:e7:
    dc:23:43:30:26:3b:d0:0f:6a:46:d0:b6:51:88:49:
    4b:c8:25:3e:b6:eb:32:cf:56:63:db:7a:79:43:35:
    0f:f7:e5:9e:fa:60:66:c7:78:f3:e8:a9:4e:c2:ed:
    3d:04:a9:c3:7d:ae:23:86:25:e9:8d:a9:5c:33:4a:
    db:16:31:85:88:b6:c1:5b:bd:02:4b:41:73:eb:c1:
    9c:d1:0b:ee:d8:74:4d:a4:ca:ab:0a:97:4d:a0:26:
    59
prime1:
    00:d7:99:be:f5:71:ee:79:b1:a0:28:77:f3:eb:10:
    ab:83:0a:f4:00:b9:76:ca:4c:d1:1a:00:97:37:de:
    32:3f:b1:5c:fb:8d:e5:ec:9b:ec:c6:10:06:2d:2e:
    13:38:97:4a:f4:f8:5d:1a:3b:9b:e2:cb:74:4d:b7:
    31:f5:22:f2:f4:c1:25:78:2d:df:ee:ca:00:0f:65:
    16:5b:fe:ea:e3:49:02:61:05:52:cd:d9:66:42:34:
    b9:d0:09:23:62:ff:c5:51:2f:b7:fb:bf:62:ed:02:
    a5:7c:85:c6:e4:db:9f:6f:4a:44:ca:41:01:40:c9:
    8b:a1:53:0f:f5:b2:4c:a0:a5
prime2:
    00:c0:3e:f9:f1:c9:a9:ed:23:7c:ae:f3:41:97:0f:
    4a:83:b6:df:e0:1f:e1:b7:4c:b8:d6:3f:79:0c:41:
    29:e9:5b:f8:95:48:8d:7d:10:8c:8b:a0:b7:09:91:
    e2:aa:35:31:60:b9:d3:88:0f:d1:33:61:0c:b3:55:
    4f:dc:ed:18:1d:d9:7a:7e:1a:e4:61:11:2c:4a:2d:
    03:3d:02:b3:e2:48:ca:e9:08:e0:bd:da:90:a4:00:
    f5:cc:ed:9b:90:5e:c0:17:e1:4e:aa:4c:94:5d:14:
    79:7a:b7:c0:39:9f:36:45:df:38:48:7d:32:48:5e:
    17:d7:ba:ea:d5:96:f7:7d:5f

Exercice : vérifiez que le produit de prime1 par prime2 donne bien modulus et que l’indicatrice d’Euler est correcte, ainsi que la clef secrète.

Ci-dssous un programme C de cryptage/décryptage utilisant ces clefs. Il utilise la librairie GMP pour les opérations sur les entiers de grande taille et se compile par la commande
gcc rsa.c -lgmp
Bien entendu, un vrai programme de cryptage/décryptage ne doit pas contenir de clé(s) privée(s) dans son code source, car le code objet les contiendra, il doit les lire dans un fichier.

#include <gmp.h>
#include <stdio.h>

const unsigned char rsa_n_tab[]={
  0xa1,0xe8,0x58,0xf4,0x81,0x34,0xc7,0x0c,0xe6,0x4e,0x12,0xa3,0x22,0xf3,
 0x2b,0x49,0xc4,0x7a,0xe0,0xc0,0xb6,0x11,0x07,0x3f,0xea,0xf3,0x23,0xeb,0xd2,
 0x3d,0x97,0x99,0x35,0x65,0xce,0x42,0xd6,0x9a,0xe9,0x53,0x3e,0x60,0xa2,0x90,
 0x28,0x7a,0xbf,0x23,0x1f,0x84,0xb0,0x10,0x4a,0x15,0x8c,0xdd,0xe1,0x29,0x43,
 0x53,0xcb,0x74,0x22,0x4f,0xf6,0xae,0xec,0x62,0x35,0x1e,0x61,0x0b,0x66,0x2e,
 0xfe,0x21,0x41,0xc1,0x56,0x6d,0x68,0x70,0x51,0x8b,0x42,0x6a,0xdb,0xca,0x13,
 0xe1,0x06,0x1d,0xdb,0xda,0x33,0x82,0x96,0x70,0x0c,0xdf,0xd6,0x4f,0x51,0xc2,
 0x51,0x57,0xae,0xec,0xa8,0x6d,0x06,0xf3,0xa3,0x02,0xb7,0x5d,0x79,0x3e,0x58,
 0x1b,0xe2,0xa2,0xe6,0xd8,0x97,0x58,0xc9,0x7f,0xf6,0x81,0x8d,0xab,0x1e,0x9b,
 0x22,0x0a,0x4f,0x77,0x41,0x49,0xe6,0x5c,0x71,0x03,0xee,0x70,0x9a,0x60,0x22,
 0x28,0x76,0xc1,0x53,0xa7,0x99,0x06,0x2a,0x22,0x9c,0x0b,0x9d,0x68,0xcc,0x2f,
 0x36,0x5a,0x84,0xbd,0xb1,0x03,0x36,0xcc,0xbe,0x07,0x4b,0x17,0x8c,0x85,0x84,
 0xef,0xd5,0x1e,0x31,0xe0,0x82,0x32,0x0e,0x52,0x85,0xd0,0x8e,0x40,0x0d,0xa8,
 0xac,0xc7,0xff,0xc1,0xc0,0xe9,0xfe,0x6c,0x49,0xa5,0xc9,0xff,0xcc,0x15,0xf1,
 0x2b,0x1c,0xc6,0x70,0x29,0x97,0x77,0xda,0x3e,0x7c,0xf7,0x3f,0xbf,0x6f,0x17,
 0x91,0xee,0xb6,0x5b,0x90,0xd8,0xba,0x49,0x07,0x96,0x1b,0xa7,0xa9,0x5e,0x37,
 0x8f,0x4c,0xbe,0xde,0xc4,0x07,0xef,0x12,0xe6,0x78,0xa0,0x4d,0x95,0x42,0x13,
 0x2e,0x3b
};

int public_key=65537;

const unsigned char pri_n_tab[]={
  0x69,0x0e,0x97,0xf2,0x07,0x88,0xd4,0x84,0x15,0x48,0xa1,0xa5,0x43,0x7f,0x60,
  0x1e,0x5c,0xa4,0x93,0x03,0xd8,0xdf,0xd1,0xc1,0x72,0xd5,0xd4,0x00,0x28,0x0a,
  0x99,0x3c,0xeb,0xbe,0x24,0x89,0x90,0x31,0x32,0xa7,0x36,0x39,0x84,0x22,0x60,
  0x71,0xcd,0x66,0xa0,0x03,0xfc,0x2e,0x85,0xb3,0xd8,0x14,0xfd,0x0e,0x46,0x46,
  0xb0,0x24,0xaa,0x43,0x12,0xc1,0x4c,0x57,0x29,0x3a,0x8e,0x23,0xd4,0x69,0x37,
  0xb3,0x22,0xb4,0xae,0x3d,0x0d,0xe0,0x9b,0xb8,0xee,0x1e,0xe2,0x81,0x0c,0x47,
  0x1e,0x2d,0xef,0xc3,0x75,0x5b,0x0d,0xfc,0xa5,0x0d,0xf5,0x44,0xc0,0xbb,0x83,
  0x06,0x8f,0x55,0xb6,0xb0,0x10,0x2b,0xb5,0x21,0x85,0x13,0xdd,0x21,0x4c,0x10,
  0xc4,0x0d,0x8a,0x17,0x0e,0x95,0xa9,0x21,0x1b,0x91,0x17,0x86,0x07,0xde,0x74,
  0xd5,0x3f,0x05,0x21,0xe7,0x57,0x67,0x3b,0x32,0xde,0x68,0xcf,0xa5,0x9b,0x61,
  0x6f,0xf7,0x89,0xc3,0xdb,0xa4,0x75,0x53,0x71,0xb8,0x77,0xab,0xb5,0x22,0xe7,
  0xdc,0x23,0x43,0x30,0x26,0x3b,0xd0,0x0f,0x6a,0x46,0xd0,0xb6,0x51,0x88,0x49,
  0x4b,0xc8,0x25,0x3e,0xb6,0xeb,0x32,0xcf,0x56,0x63,0xdb,0x7a,0x79,0x43,0x35,
  0x0f,0xf7,0xe5,0x9e,0xfa,0x60,0x66,0xc7,0x78,0xf3,0xe8,0xa9,0x4e,0xc2,0xed,
  0x3d,0x04,0xa9,0xc3,0x7d,0xae,0x23,0x86,0x25,0xe9,0x8d,0xa9,0x5c,0x33,0x4a,
  0xdb,0x16,0x31,0x85,0x88,0xb6,0xc1,0x5b,0xbd,0x02,0x4b,0x41,0x73,0xeb,0xc1,
  0x9c,0xd1,0x0b,0xee,0xd8,0x74,0x4d,0xa4,0xca,0xab,0x0a,0x97,0x4d,0xa0,0x26,
  0x59
};

void tabunsignedchar2mpz(const unsigned char tab[],int len,mpz_t & res){
  mpz_set_si(res,0);
  for (int i=0;i<len;++i){
    mpz_mul_si(res,res,256); // res=256*res;
    mpz_add_ui(res,res,tab[i]); // res+=tab[i];
  }
}

// ./rsa crypt|decrypt filein fileout
int main(int argc,const char ** argv){
  if (argc<4){
    printf("Usage: %s c|d filein fileout\nc crypt, d decrypt\n",argv[0]);
    return 1;
  }
  mpz_t z,p,n;
  mpz_init(z); mpz_init(p); mpz_init(n);
  tabunsignedchar2mpz(rsa_n_tab,sizeof(rsa_n_tab),n);
  if (argv[1][0]=='c'){
    printf("crypting\n");
    mpz_set_si(p,public_key);
  }
  else {
    tabunsignedchar2mpz(pri_n_tab,sizeof(pri_n_tab),p);
    printf("decrypting\n");
    mpz_out_str(stdout,10,n);
    printf("\n");
    mpz_out_str(stdout,10,p);
    printf("\n");
  }
  FILE * f=fopen(argv[2],"r");
  int res=mpz_inp_str(z,f,0);
  fclose(f);
  mpz_powm(z,z,p,n);
  f=fopen(argv[3],"w");
  mpz_out_str(f,10,z);
  fclose(f);
  mpz_clear(z); mpz_clear(p); mpz_clear(n);
}

4.9.3  Sur quoi repose la sécurité de RSA.

Une référence sur l’utilisation de RSA pour la sécurisation des transactions par carte bancaire.

4.10  Pour aller plus loin

Les polynômes de /p[X]\mathbb{Z}/p\mathbb{Z}[X] modulo un polynôme irréductible de degré nn forment un corps fini de cardinal une puissance d’un nombre premier p np^n. On montre que les corps de cardinal fini sont tous de cette forme.

La recherche de solutions a 2=b 2(modn)a^2=b^2 \pmod n est un objectif de pas mal d’algorithmes de factorisation, par exemple cfrac (fractions continues), le crible quadratique, et même l’algorithme de Shor (pour ordinateur quantique).

5  Algèbre linéaire

5.1  Systèmes linéaires sur /p\mathbb{Z}/p\mathbb{Z}, pivot de Gauss

Les systèmes linéaires sur /p\mathbb{Z}/p\mathbb{Z} se résolvent comme les systèmes linéaires à coefficients réels ou complexes, par combinaison linéaire d’équations. On peut encoder par une matrice, en créant une ligne de matrice par équation et en y stockant les coefficients des inconnues. Les combinaisons linéaires d’équations correspondent alors à des combinaisons linéaires de lignes de la matrice. L’algorithme du pivot de Gauss consiste à triangulariser la matrice de manière équivalente (i.e. avec des opérations réversibles).

Algorithme du pivot de Gauss (sur un corps)
Soit MM une matrice ayant LL lignes et CC colonnes. On numérote à partir de 0.

  1. initialiser les indices du pivot ligne ll et cc à 0.
  2. Tant que l<Ll&lt;L et c<Cc&lt;C faire :
    1. Parcourir la colonne cc à partir de la ligne ll et chercher un coefficient non nul.
      Si tous les coefficients sont nuls, incrémenter cc de 1 et passer à l’itération suivante de la boucle Tant que.
    2. Si nécessaire, échanger la ligne ayant un coefficient non nul avec la ligne ll
    3. Maintenant M l,c0M_{l,c}\neq 0. Si M l,c1M_{l,c} \neq 1 on effectue : L l1M l,cL lL_l \leftarrow \frac{1}{M_{l,c}} L_l Cette opération est réversible car on multiplie la ligne par un coefficient non nul.
    4. Pour jj de l+1l+1 à L1L-1 effectuer la manipulation de ligne sur la matrice MM (dont la ligne ll a été modifiée) : L jL jM j,cL l,L_j \leftarrow L_j - M_{j,c} L_l, Cette opération crée un 0 à la ligne jj en colonne cc sans détruire les 0 existants en ligne jj pour des indices de colonne strictement inférieurs à cc.
      Cette opération est réversible d’inverse L jL j+M j,cL lL_j \leftarrow L_j+M_{j,c} L_l (avec le coefficient de la matrice avant l’opération)
    5. Incrémenter ll et cc de 1.

Toutes les opérations sont réversibles, donc le système obtenu est équivalent au système initial. Mais il est plus simple, car triangulaire supérieur, il y a des 0 en-dessous d’une “diagonale généralisée” (normalement c’est une diagonale, mais elle peut se décaler vers la droite s’il y a des colonnes où on n’a pas trouvé de 0). On résoud alors le système de bas en haut.

On peut aussi faire l’étape 2d pour jj de 0 jusque L1L-1 et jLj\neq L et obtenir une matrice échelonnée, avec des 0 de part et d’autre de la diagonale, plus précisément des 0 dans toutes les colonnes correspondant à des coefficients non nuls sur la diagonale généralisée.

def piv(M):
    L,C=dim(M)
    l,c=0,0
    while l<L and c<C:
        for j in range(l,L):
            if M[j,c]!=0: 
                break
        if j==L:
            c += 1
            continue
        if j!=l: 
            M[j],M[l]=M[l],M[j]
        if M[l,c]!=1:
            M[l]=inv(M[l,c])*M[l]
        for j in range(l+1,L):
            M[j] -= M[j,c]*M[l]
        c += 1
        l += 1
    return M

onload


Donc : {2x +2y +3z =4[11] x 2y +z =2[11] 2x +2y +3z =5[11]{x +y 4z =2[11] y +2z =0[11] z =1[11]\left\{ \begin{array}{cccc} 2x&+2y&+3z&=4 [11]\\ x&-2y&+z&=2 [11] \\ -2x&+2y&+3z&=5 [11] \end{array} \right. \Leftrightarrow \left\{\begin{array}{cccc} x&+y&-4z&=2 [11]\\ &y&+2z&=0 [11] \\ & &z&=1 [11] \end{array} \right. sa solution s’obtient en résolvant l’équation correspondant à la dernière ligne de la matrice réduite, donc z=1[11]z=1 [11], puis l’avant-dernière ligne donne y=2z=2[11]y=-2z=-2[11] puis la première équation donne x=y+4z+2=8[11]=3[11]x=-y+4z+2=8[11]=-3[11].
On vérifie

Prolongement:
L’algorithme du pivot de Gauss peut s’effectuer directement sur la matrice AA d’un système sans son second membre, c’est la factorisation PA=LUPA=LU d’une matrice, qui permet de se ramener à résoudre deux systèmes triangulaires.

Optimisations

5.2  Algèbre linéaire sur /p\mathbb{Z}/p\mathbb{Z}

La théorie des espaces vectoriels sur ,,\mathbb{R}, \mathbb{Q}, \mathbb{C} se généralise à un corps fini. Les définitions et propriétés d’espace vectoriel, de dimension finie, de famille libre, génératrice, de bases, d’applications linéaires, de matrice d’applications linéaires sont identiques.

Définition 1   Un espace vectoriel VV sur un corps KK est un ensemble muni d’une loi d’addition de deux vecteurs (VV est un groupe commutatif pour +), et d’une multiplication d’un vecteur par un scalaire de KK, qui est distributive (a.(u+v)=a.u+a.va.(u+v)=a.u+a.v et (a+b).u=a.u+b.u(a+b).u=a.u+b.u) et vérifie a.(b.v)=(a.b)va.(b.v)=(a.b)v et 1.v=v1.v=v.

On montre qu’un sous-ensemble WW de VV stable par addition (si w 1,w 2Ww_1,w_2 \in W alors w 1+w 2Ww_1+w_2 \in W) et par multiplication par une constante de KK (si wWw \in W alors a.wWa.w \in W) est un espace vectoriel, on dit que c’est un sous-espace vectoriel de VV.

Dans la suite, on n’écrira plus explicitement le signe de multiplication entre un scalaire et un vecteur.

Exemples d’espaces vectoriels: (/p) d(\mathbb{Z}/p\mathbb{Z})^d (qui est en bijection avec les entiers naturels plus petits que p dp^d, mais attention l’addition des entiers ne correspond pas à l’addition dans l’espace vectoriel), les polynômes de degré inférieur à dd à coefficients dans /p\mathbb{Z}/p\mathbb{Z}, les matrices à coefficients dans /p\mathbb{Z}/p\mathbb{Z}

Définition 2   Soit VV un espace vectoriel sur un corps fini K=/pK=\mathbb{Z}/p\mathbb{Z} et F={v 1,...,v k}F=\{ v_1, ... ,v_k\} une partie de kk éléments de VV.
Une combinaison linéaire de
FF est un vecteur de la forme a 1v 1+...+a kv ka_1 v_1 +... + a_k v_k avec les a iKa_i \in K. On vérifie que l’ensemble des combinaisons linéaires de FF est un sous-espace vectoriel de VV noté Vect(F)(F).
On dit que
FF est libre si aucun des vecteurs de FF n’est combinaison linéaire des autres ce qui revient à dire que la relation a 1v 1+...+a kv k=0a_1 v_1 +... + a_k v_k=0 entraine a 1=...=a k=0a_1=...=a_k=0.
Une famille
FF est génératrice si tout vecteur de VV peut s’écrire comme combinaison linéaire des éléments de FF.
Un espace vectoriel est de dimension finie s’il admet une famille génératrice (ayant un nombre fini d’éléments).

D’une famille génératrice finie FF (non réduite à des vecteurs nuls), on peut extraire une famille génératrice et libre. En effet si FF est libre, c’est terminé. Sinon, l’un des vecteurs de FF est combinaison linéaire des autres, on peut l’enlever de FF en conservant l’espace vectoriel engendré. Comme le nombre d’éléments de FF est fini, on s’arrête.

Par exemple {1,x,1+x 2,x 2}\{ 1,x,1+x^2,x^2\} est une famille génératrice de Vect({1,x,1+x 2,x 2}\{ 1,x,1+x^2,x^2\}) et on peut éliminer 1+x 21+x^2 de la famille pour la rendre libre (on peut aussi éliminer 1 ou éliminer x 2x^2).

Définition 3   Un espace vectoriel de dimension finie possède une famille génératrice et libre, on l’appelle base (dans le cas particulier de V={0}V=\{0\}, la dimension de VV est nulle et on peut définir la base de VV comme étant l’ensemble vide). Toutes les bases ont le même nombre d’éléments dd, appelé dimension de VV. VV possède p dp^d éléments.

En effet, tout élément de VV peut sécrire de manière unique comme combinaison linéaire des éléments de la famille. Les dd composantes sur une base fixée peuvent prendre pp valeurs chacune, il y a donc p dp^d éléments dans VV. C’est une grande différence avec \mathbb{R} (qui simplifie la preuve que toutes les bases ont le même nombre d’éléments).

Proposition 4   Une famille libre d’un espace vectoriel de dimension dd possède au plus dd éléments, si elle en possède dd c’est une base. Une famille génératrice possède au moins dd éléments, si elle en possède dd c’est une base.

Sinon, on aurait pour les combinaisons linéaires d’une famille libre trop d’éléments, et pour les combinaisons linéaires d’une famille génératrice pas assez.

Définition 5   On appelle base canonique de (/p) d(\mathbb{Z}/p\mathbb{Z})^d la base {(1,0..0),(0,1,0..),...(0...,0,1)}\{(1,0..0),(0,1,0..),...(0...,0,1)\}. On appelle base canonique des polynômes de degré inférieur strict à dd la base {1,x,...,x d1}\{ 1,x,...,x^{d-1} \}. Plus généralement, on parle de base canonique pour une base intrinsèque (ou naturelle) d’un espace vectoriel.
Définition 6   Soient VV et WW deux espaces vectoriels. Une application linéaire φ:VW\varphi: V \rightarrow W est une application qui respecte la linéarité, i.e. telle que φ(u+v)=φ(u)+φ(v)\varphi(u+v)=\varphi(u)+\varphi(v) et φ(av)=aφ(v)\varphi(a v)=a\varphi(v).

Exemples : (x,y)(x+y,xy)(x,y) \rightarrow (x+y,x-y) la multiplication par xx des polynômes de degré inférieur à 2 (P 2P_2) dans les polynômes de degré inférieur à 3 (P 3P_3), ou la dérivée de P 3P_3 dans P 2P_2.

Si VV et WW sont de dimension finie, VV de base {v 1,..,v d}\{v_1,..,v_d\} et WW de base {w 1,..,w δ}\{w_1,..,w_\delta\} on peut caractériser φ\varphi par les coordonneées des φ(v j)\varphi(v_j) dans la base {w 1,...,w δ}\{ w_1,...,w_\delta\}. On appelle matrice de φ\varphi dans ces bases le tableau des coordonnées des φ(v j)\varphi(v_j) en colonnes. On a alors M=(m ij) 1iδ,1jd,φ(v j)= i=1 δm i,jw jM=(m_{ij})_{1 \leq i \leq \delta , 1 \leq j \leq d}, \quad \varphi(v_j)=\sum_{i=1}^\delta m_{i,j} w_j On vérifie que si vv a pour coordonnées le vecteur colonne VV, alors φ(v)\varphi(v) a pour coordonnées le vecteur colonne W=MVW=MV

Exercice : calculer les matrices des exemples ci-dessus.

Soit vVv \in V, les coordonnées de φ(v)\varphi(v) dans la base {w 1,..,w δ}\{w_1,..,w_\delta\} s’obtiennent alors en faisant le produit de la matrice AA de φ\varphi par le vecteur colonne des coordonnées de vv dans la base {v 1,..,v d}\{v_1,..,v_d\}

La somme de 2 matrices correspond alors à la somme de deux applications linéaires ayant ces matrices, la multiplication d’une matrice par un élément de /p\mathbb{Z}/p\mathbb{Z} correspond à la multiplication de l’application linéaire correspondante par cet élément. Le produit de deux matrices correspond à la composition de deux applications linéaires (c’est d’ailleurs une manière de prouver que le produit de deux matrices est associatif).

Définition 7   On appelle transposée de la matrice A=(a ij) 1in,1jmA=(a_{ij})_{1 \leq i \leq n,1\leq j \leq m} ayant nn lignes et mm colonnes la matrice A *=(a ji) 1jm,1inA^*=(a_{ji})_{1\leq j \leq m ,1 \leq i \leq n} ayant mm lignes et nn colonnes.
Définition 8   Noyau et image d’une application linéaire φ:VW\varphi: V \rightarrow W.

L’ensemble des vecteurs vVv \in V tels que φ(v)=0\varphi(v)=0 est un sous-espace vectoriel de VV appelé noyau de φ\varphi et noté Ker(φ)(\varphi). Par abus, on note aussi Ker(A)(A) le noyau de l’application linéaire de matrice AA dans les bases canoniques de VV et WW. Une application linéaire est injective si et seulement si son noyau est réduit au vecteur nul.

L’ensemble des images φ(v),vV\varphi(v), v \in V est un sous-espace vectoriel de WW appelé image de φ\varphi et noté Im(φ)(\varphi). De même, on parle de Im(A)(A) par abus de notation. La dimension de l’image est appelé range de φ\varphi (ou de la matrice AA).

Théorème 9   (Théorème du rang)
dim(V)(V)=dim(Ker(φ\varphi)) + dim(Im(φ\varphi))

Preuve : on utilise le procédé de création d’une base à partir d’une famille génératrice de Im(φ\varphi) obtenue en prenant les images des vecteurs d’une base de VV, i.e. on transpose la matrice AA de l’application linéaire et on applique Gauss. Les combinaisons linéaires sur la base de VV de l’algorithme de Gauss forment toujours une base de VV. Les lignes nulles de A *A^* correspondent à des vecteurs qui forment une base de Ker(φ)(\varphi), les lignes non nulles forment une base de Im(φ)(\varphi).

Définition 10   Une application linéaire de VV (espace vectoriel de dimension dd) dans lui-même φ:VV\varphi: V \rightarrow V est appelé endomorphisme. Si un endomorphisme est injectif alors il est inversible. La matrice AA de φ\varphi et la matrice BB de son application réciproque vérifient alors AB=BA=I dAB=BA=I_d

L’algorithme du pivot de Gauss permet de traiter tous les aspects calculatoires :

5.3  Application : cryptographie de Hill

Au lieu de coder un message caractère par caractère, ce qui est sujet à des attaques par fréquence, on décide de grouper les caractères par dd-uplets et on les considère comme des vecteurs vv de (/p) d(\mathbb{Z}/p\mathbb{Z})^d (en fait, il n’est pas indispensable de travailler sur un corps, on peut aussi travailler sur un anneau /n\mathbb{Z}/n\mathbb{Z}). On choisit une matrice carrée AA de taille dd inversible sur ce corps qui sera la clef secrète de chiffrement. Le message crypté correspondant au message en clair vv est alors le message w=Avw=Av. On retrouve vv connaissant le message crypté ww et la clef de chiffrement en calculant v=A 1wv=A^{-1}w.

Pour assurer la sécurité du cryptage, il faut bien sur prendre pp et dd assez grands pour que l’ensemble des matrices possibles soit suffisamment grands (il y a p d 2p^{d^2} matrices possibles, et elles ne sont pas toutes inversibles, la première colonne ne doit pas être nulle, la deuxième ne doit pas être dans l’espace engendré par la première, etc. il y a donc (p d1)(p dp)(p dp 2)...(p dp d1)(p^d-1)(p^d-p)(p^d-p^2)...(p^d-p^{d-1}) matrices inversibles). On peut aussi décider d’éviter les attaques par fréquence en prenant une partie de dd-uplet dans le message à coder, et une autre partie avec des caractères aléatoires (le message crypté sera plus long mais le cryptage sera plus résistant).

5.4  Application aux codes

Cette section ne sera peut-être plus au programme à partir de 2021/22 en raison de la diminution de l’horaire de l’UE
On travaillera dans cette section avec p=2p=2, i.e. sur le corps K=/2K=\mathbb{Z}/2\mathbb{Z}, mais les définitions et énoncés restent valables dans /p\mathbb{Z}/p\mathbb{Z} ou dans un corps fini plus général.

On appellera symbole d’information l’unité de base transmise, qu’on supposera appartenir à un corps fini KK, par exemple pour un bit un élément de K=/2K=\mathbb{Z}/2\mathbb{Z} (pour un octet, il faudrait prendre un élément du corps à 256 éléments K=F 256=F dK=F_{256}=F_d).

On veut coder un message de longueur kk avec des possibilités de détection et de correction d’erreurs, pour cela on rajoute des symboles calculés à partir des précédents, on envoie un élément d’un code ayant nn symboles.

5.4.1  Code de répétition

Par exemple, on peut décider de répéter 3 fois chaque symbole n=3kn=3k. Le récepteur du message peut détecter des erreurs de transmission (sauf si la même altération se produit 3 fois de suite, ce qui est très improbable). Il peut corriger des erreurs par exemple s’il reçoit 2 fois le même symbole aa et un 3ième symbole distinct bb, il garde le symbole aa. Mais ce code simple est assez couteux à transmettre, on envoie 3 fois plus de données qu’il n’y a d’informations. On va présenter des méthodes plus efficaces.

5.4.2  Le bit de parité.

On prend k=7k=7 bits et n=8n=8 bits. On compte le nombre de 1 parmi les 7 bits envoyés, si ce nombre est pair, on envoie 0 comme 8ième bit, sinon 1. Au final le nombre de bits à 1 de l’octet (1 octet=8 bits) est pair. On peut ainsi détecter une erreur de transmission si à la réception le nombre de bits d’un octet est impair, mais on ne peut pas corriger d’erreurs.

5.4.3  Codes linéaires

Définition 11   On multiplie le vecteur vK kv \in K^k des kk symboles à gauche par une matrice MM à coefficients dans KK de taille n×kn \times k et on transmet l’image MvK nMv \in K^n.

L’ensemble des mots de code est l’image de MM, c’est un sous-espace vectoriel de K nK^n. La matrice de MM d’un code linéaire s’obtient en calculant les images des vecteurs de la base canonique.

Exemple : le code de répétition est un code linéaire de paramètres k=1,n=3k=1,n=3, de matrice la matrice colonne (1 1 1)\begin{pmatrix} 1\\1\\1\end{pmatrix} . Le bit de parité est un code linéaire de paramètres k=7,n=8k=7,n=8, de matrice


Remarque : On utilise ici la convention de l’algèbre linéaire (avec des vecteurs colonnes et multiplication par une matrice à gauche). Certains auteurs utilisent l’autre convention, i.e. des vecteurs lignes et multiplication par la matrice à droite, tout est transposé. Ceci vient peut-être du fait qu’il est plus facile de saisir un vecteur ligne qu’un vecteur colonne avec certains logiciels. Xcas utilise la convention que si on multiplie une matrice par un vecteur ligne, alors le vecteur ligne est automatiquement transformé en vecteur colonne (il n’y a en effet aucune confusion possible).

Pour assurer qu’on peut identifier un antécédent unique à partir d’une image, il faut que MM corresponde à une application linéaire injective, ce qui entraine nkn\geq k. On dit qu’un vecteur de nn symboles est un mot du code s’il est dans l’image de l’application linéaire.

Pour assurer l’injectivité tout en facilitant le décodage, on utilise très souvent une matrice identité k,kk,k comme sous-bloc de la matrice n,kn,k, par exemple on prend l’identité pour les kk premières lignes de MM, on ajoute ensuite nkn-k lignes. On parle de code systématique.

Pour savoir si un vecteur est un mot de code, il faut vérifier qu’il est dans l’image de MM. On peut par exemple vérifier qu’en ajoutant la colonne de ses coordonnées à MM, on ne change pas le rang de MM (qui doit être kk), mais c’est assez couteux. On préfère déterminer une matrice de controle HH telle que : xIm(M)Hx=0x \in \mbox{Im}(M) \Leftrightarrow Hx=0

Proposition 12   Pour un code systématique de matrice M=(I k C)M=\begin{pmatrix} I_k \\ C \end{pmatrix} (composée de l’identité I kI_{k} et d’une matrice CC sur ses nkn-k dernières lignes), alors la matrice de contrôle est H=(C,I nk)H=(-C,I_{n-k})

En effet : Mv=x=(v Cv)Mv=x=\begin{pmatrix} v\\Cv\end{pmatrix} donc : Hx=(C,I nk)(v Cv)=0Hx=(-C,I_{n-k}) \begin{pmatrix} v\\Cv\end{pmatrix} = 0

Exemple, pour le bit de parité, C=(1,1,1,1,1,1,1)C=(1,1,1,1,1,1,1) et H=(1,1,1,1,1,1,1,1)H=(1,1,1,1,1,1,1,1).

5.4.4  Codes polynomiaux

Il s’agit d’un cas particulier de codes linéaires.

Définition 13   On se donne un polynôme g(x)g(x) de degré nkn-k, On représente le message de longueur kk à coder par un polynôme PP de degré k1k-1. On envoie alors PgPg.

Le récepteur peut controler que le reste de la division euclidienne du polynôme reçu par gg est nul, et extraire l’information qui est le quotient de la division euclidienne par gg. Ce code n’est pas systématique, mais on peut facilement l’adapter pour le rendre systématique.

Définition 14   On multiplie PP par x nkx^{n-k}, on calcule le reste RR de la division de Px nkP x^{n-k} par gg. On émet alors Px nkRP x^{n-k}-R qui est divisible par gg.

Les mots de code sont les polynômes divisibles par gg.

Exemple : le bit de parité correspond à k=7,n8,g=x+1k=7, n-8, g=x+1. Le polynôme g=x 7+x 3+1g=x^7+x^3+1 (k=120,n=127k=120,n=127) était utilisé par le Minitel.

5.4.5  Erreurs

Si le mot recu n’est pas dans l’image de l’application linéaire il y a eu erreur de transmission. Sinon, il n’y a pas eu d’erreur détectable (il pourrait y avoir eu plusieurs erreurs qui se “compensent”).

Plutôt que de demander la réémission du mot mal transmis (ce qui serait par exemple impossible en temps réel depuis un robot en orbite autour de Mars), on essaie d’ajouter suffisamment d’information pour pouvoir corriger des erreurs en supposant que leur nombre est majoré par NN. Si les erreurs de transmissions sont indépendantes, la probabilité d’avoir au moins N+1N+1 erreurs dans un message de longueur LL est k=N+1 L(Lk)ε k(1ε) Lk\sum_{k=N+1}^L \left( ^L_{k} \right) \epsilon^{k} (1-\epsilon)^{L-k}, où ε\epsilon est la probabilité d’une erreur de transmission, c’est aussi 1-binomial_cdf(L,epsilon,N). Par exemple, pour un message de 10 310^3 caractères, chacun ayant une probabilité d’erreur de transmission de 10 310^{-3}, si on prend N=3N=3, alors la probabilité d’avoir au moins 4 erreurs est de 0.019 (arrondi par excès) :

ou directement

5.4.6  Distance

Définition 15   La distance de Hamming de 2 mots est le nombre de symboles qui diffèrent. (il s’agit bien d’une distance au sens mathématique, elle vérifie l’inégalité triangulaire).

Exercice : écrire une procédure de calcul de la distance de Hamming de 2 mots. En Xcas, la fonction s’appelle hamdist.

La distance d’un code est la distance de Hamming minimale de 2 mots différents du code. Pour un code linéaire, la distance est aussi le nombre minimal de coefficients non nuls d’un vecteur non nul de l’image. Pour un code polynomial, la distance du code est le nombre minimal de coefficients non nuls d’un multiple de gg de degré inférieur à nn.

Majoration de la distance du code:

Proposition 16   (borne de Singleton)
La distance minimale d’un code linéaire est inférieure ou égale à
m+1m+1m=nkm=n-k est le nombre de symboles ajoutés.

Preuve : On écrit en ligne les coordonnées des images de la base canonique (ce qui revient à transposer la matrice) et on réduit par le pivot de Gauss, comme l’application linéaire est injective, le rang de la matrice est kk, donc la réduction de Gauss crée k1k-1 zéros dans chaque ligne, donc le nombre de coefficients non nuls de ces kk lignes (qui sont toujours des mots de code) est au plus de nk+1n-k+1.

Remarque : la borne n’est pas toujours atteinte, par exemple un code de paramètres n=7,k=4n=7,k=4 a une distance minimale d’au plus 3 (et non 4).

5.4.7  Correction au mot le plus proche

Une stratégie de correction basée sur la distance consiste à trouver le mot de code le plus proche d’un mot donné. Si la distance d’un code est supérieure ou égale à 2t+12t+1, et s’il existe un mot de code de distance inférieure à tt au mot donné, alors ce mot de code est unique. On corrige alors le mot transmis en le remplaçant par le mot de code le plus proche. Il n’est pas toujours possible de corriger un mot de code, sauf si la propriété suivante est vérifiée :

Définition 17   On dit qu’un code tt-correcteur est parfait si la réunion des boules de centre un mot de code et de rayon tt (pour la distance de Hamming) est disjointe et recouvre l’ensemble des mots (K nK^n).

Pour t=1t=1 et K=/2K=\mathbb{Z}/2\mathbb{Z}, la boule de rayon 1 de K nK^n a pour cardinal 1+n1+n (on est soit au centre, soit une des nn composantes est différente de celle du centre), on doit donc avoir : (1+n)2 k=2 nn=2 m1,m=nk(1+n)2^k=2^n \Leftrightarrow n=2^m-1, \quad m=n-k On a aussi nk+12t+1=3n-k+1 \geq 2t+1=3 donc m2m \geq 2. Les codes possibles sont donc:

Ces codes 1 correcteurs parfaits sur /2\mathbb{Z}/2\mathbb{Z} sont appelés codes de Hamming binaires. On les construit pour pouvoir corriger facilement une erreur avec la matrice de contrôle. Par exemple pour m=4m=4, il y a une seule matrice de controle de taille (15,4)(15,4) telle que HxHx donne la position de l’erreur (en base 2), elle est obtenue en écrivant les entiers de 1 à 15 en base 2 H=(0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1)H=\left( \begin{array}{ccccccccccccccc} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 \\ 0 & 0 & 0 & 1 & 1 & 1 & 1 & 0 & 0 & 0 & 0 & 1 & 1 & 1 & 1 \\ 0 & 1 & 1 & 0 & 0 & 1 & 1 & 0 & 0 & 1 & 1 & 0 & 0 & 1 & 1 \\ 1 & 0 & 1 & 0 & 1 & 0 & 1 & 0 & 1 & 0 & 1 & 0 & 1 & 0 & 1 \end{array} \right) on déplace les colonnes de la matrice identité (colonnes 1, 2, 4, 8) en fin pour écrire K=(C,I 4)K=(-C,I_4), le code correspondant est (I 11 C)\begin{pmatrix} I_{11}\\ C \end{pmatrix}, il permet de corriger une erreur, on calcule HxHx et si le résultat est non nul, on change le bit d’indice HxHx en tenant compte de la permutation d’indices sur les colonnes de HH.

5.5  Prolongement

On peut construire des corps finis GF(2,n) ayant 2 n2^n éléments en prenant les classes de congruence de polynômes à coefficients dans /2\mathbb{Z}/2 \mathbb{Z} pour la division euclidienne par un polynôme fixé de degré nn qui n’admet pas de diviseur non trivial (comme pour les entiers premiers). Pour certains polynômes bien choisis, on peut fixer la distance de Hamming du code correspondant à 2t+12t+1 pour tt quelconque et donc corriger jusqu’à tt erreurs.

A  Programmes

A.1  Triangle de Pascal pour calculer les coefficients binomiaux

# on calcule une ligne en fonction de la precedente
def Pascal(n):
    # local tableau,ligne,prec,j,k;
    tableau=[[1]]
    for j in range(1,n+1):
        ligne=[1]
        prec=tableau[j-1]
        for k in range(1,j):
            ligne.append(prec[k-1]+prec[k])
        ligne.append(1)
        tableau.append(ligne);
    return tableau

onload

A.2  PGCD itératif avec reste symétrique

def pgcd(a,b):
    while b!=0:
         a,b=b,abs(mods(a,b))
    return abs(a) # abs est necessaire si b==0 au debut

onload
En Python, il faut aussi définir la fonction mods :

def mods(a,b):
    if b<0:
        b = -b
    a %= b
    if 2*a>b:
        return a-b
    return a

  

Retour à la page principale de Giac/Xcas.