Expliquer un corps fini

Magazine
Marque
GNU/Linux Magazine
n°
Numéro
195
Mois de parution
juillet 2016
Spécialité(s)


Résumé

Pour corriger un code QR avec erreur ou rature, on a besoin de savoir manipuler un corps fini prĂ©cis, đ”œ256, encore faut-il savoir de quoi on parle.


Body

En vue de corriger les codes QR dĂ©corĂ©s ou abĂźmĂ©s (voir articles prĂ©cĂ©dents [1]), on a besoin de notions mathĂ©matiques plus poussĂ©es. Cet article va prĂ©senter la notion mathĂ©matique de corps fini, sur laquelle le code correcteur de Reed-Solomon est construit. On construira en Python le corps â„€/pâ„€ oĂč p est un nombre premier. Les codes sont disponibles sur GitHub [2].

Cet article parlera de mathĂ©matiques, contrairement aux autres. On prĂ©sentera ce qu’est, de maniĂšre gĂ©nĂ©rale, un corps puis nous construirons un corps fini premier pour, dans l’article suivant, obtenir le corps dont nous avons besoin et les structures algĂ©briques utiles Ă  l’aide de briques orientĂ©es objet.

1. Un peu de mathĂ©matiques

Nous commençons par dĂ©finir ce qu’est un corps avant de nous intĂ©resser Ă  une construction possible.

1.1. Qu’est-ce qu’un corps ?

En gros, un corps est un ensemble de « nombres Â» dans lequel on peut effectuer les quatre opĂ©rations sans se demander s’il n’y a pas un loup (dans Paris) ou une impossibilitĂ© (Ă  part, bien sĂ»r, diviser par 0).

Formellement, on se donne un ensemble K sur lequel on a deux opĂ©rations notĂ©es habituellement + et ×.

L’addition a les propriĂ©tĂ©s suivantes :

  • Elle est commutative : a+b=b+a pour tous a et b de K ;
  • Elle est associative : (a+b)+c=a+(b+c) quels que soient a, b, c∈K ce qui veut dire en pratique qu’on n’est pas tenu d’utiliser ni d’écrire des parenthĂšses ;
  • La loi possĂšde un Ă©lĂ©ment neutre 0 pour lequel ∀a∈K, a+0=0+a=a ;
  • Tout Ă©lĂ©ment a a un « inverse Â» b qu’on appelle opposĂ©, c’est-Ă -dire que a+b=b+a=0. On note b=−a et a−b=a+(−b).

Ceci veut dire que (K,+) est un groupe commutatif.

(K*,×) est aussi un groupe, non nĂ©cessairement commutatif (K* signifie K sans 0). L’inverse de a≠0 est notĂ© 1/a ou a−1 puisque a−1×a=a×a−1=1.

Enfin, la multiplication est distributive par rapport Ă  l’addition, ce qui veut dire que ∀a, b, c∈K, (a+b)×c=a×c+b×c et de mĂȘme Ă  gauche. Par convention, la multiplication est prioritaire sur l’addition, ce qui veut dire que a+b×c=a+(b×c).

On a tous dĂ©jĂ  calculĂ© dans des corps comme ℝ (l’ensemble des nombres rĂ©els) ou ℂ (les nombres complexes utilisĂ©s par exemple en Ă©lectronique analogique). Ces deux derniers corps contiennent tous les deux le corps ℚ des fractions des entiers relatifs, c’est mĂȘme le plus petit sous-corps qu’ils contiennent (on appelle ce sous-corps leur sous-corps premier). Ces trois corps sont infinis.

En revanche, l’ensemble â„€ des entiers relatifs n’est pas un corps car seuls 1 et −1 ont des inverses dans â„€ pour la multiplication (qui sont eux-mĂȘmes), â„€ est cependant un anneau muni d’une division euclidienne.

Les flottants utilisĂ©s en informatique munis des opĂ©rations + et × ne forment pas un corps, mĂȘme pas un anneau, rien en fait. Il faut le savoir quand on fait du calcul scientifique Ă  cause de mantisses et d’exposants de taille limitĂ©e. Ainsi, 10100+10−100−10100=
 eh bien ça vaut 0 pour une machine qui ne fait pas de calcul formel. C’est un pont aux Ăąnes de prof de maths qui n’empĂȘche pas de travailler avec en prenant quelques prĂ©cautions. Par exemple, pour une calculette de poche couramment rencontrĂ©e en classe, 10n+10−n−10n vaut 0 dĂšs que nâ©Ÿ7. [3]

1.2 Premiers corps finis

Évidemment, un corps fini ne contient pas ℚ, ilest construit autrement.

On commence par choisir un nombre premier p, le reste de la division d’un nombre entier relatif par p est donc entre 0 et p−1. VoilĂ  notre premier corps fini : {0,1,
,p−1} muni de l’addition et de la multiplication modulo p qu’on note â„€/pâ„€ ou â„€p, parfois â„€/p. Les corps finis ont tous pour sous-corps premier un corps fini du type â„€/pâ„€ avec p premier (p est sa caractĂ©ristique).Pour distinguer les Ă©lĂ©ments de â„€/pâ„€ des nombres entiers classiques, on les Ă©crira surmontĂ©s d’une barre.

Ainsi, modulo 7 : Formule_1 car 11=1×7+4 et Formule_2.

Attention, si n n’est pas un nombre premier, â„€/nâ„€ n’est pas un corps. Par exemple, si n=4Formule_3 n’a pas d’inverse puisque Formule_4Formule_5 est un diviseur de Formule_6. Dans ce cas, les seuls nombres q qui ont un inverse sont ceux qui n’ont pas de facteur premier commun avec n, en d’autres mots, si PGCD(n,q)=1. On dit que â„€/nâ„€ est un anneau (oĂč Ă  part l’absence Ă©ventuelle d’inverse pour la multiplication, la structure algĂ©brique est la mĂȘme, et en plus sans division euclidienne).

Qu’en est-il de la division, par exemple par Formule_7 ?

Si le corps est petit, on peut chercher l’inverse Ă  la mainautrement dit par force brute : Formule_8. Donc diviser par Formule_7 revient Ă  multiplier par son inverse qui est Formule_5. L’algorithme efficace utilisĂ© dans notre cas sera l’algorithme d’Euclide Ă©tendu qui donne les coefficients de Bezout u et v dans l’équation 4×u+7×v=1 (oĂč u et v sont des entiers relatifs). En fait, seul u nous est utile, on l’obtient facilement Ă  la main par la mĂ©thode de Blankinship (ou pivot de Gauß) [4] oĂč on effectue des opĂ©rations Ă©lĂ©mentaires (combinaisons linĂ©aires ou permutations) sur les lignes de la matrice suivante :

formule

La premiĂšre colonne contient un seul 1 et le reste est constituĂ© de 0, on a terminĂ© l’algorithme : u=2, v=−1 et 7×(−1)+2×4=1 donc l’inverse de Formule_7 est bien Formule_3 dans â„€/7â„€.

Cette mĂ©thode fonctionne aussi quand on veut calculer le PGCD de plusieurs nombres etleurs coefficients de Bezout.Ainsi, si la matrice Ă  chaque Ă©tape est Mi,j, pour tout i∈{1;2}, Mi,1=7×Mi,2+4×Mi,3 pendant tout le dĂ©roulement de l’algorithme.

Voici la fonction qui retourne l’inverse de a modulo n, elle se trouve dans qrcodeoutils.py :

01: def bezout(n,a):
02:  r,u,v,rr,uu,vv=a,1,0,n,0,1
03:  while rr!=0:
04:  q=r//rr
05:  r,u,v,rr,uu,vv=rr,uu,vv,r-q*rr,u-q*uu,v-q*vv
06:  return u%n,v%n

Il s’agit ici de l’algorithme classique qu’on peut allĂ©ger des variables v et vv qui n’ont aucune utilitĂ© dans la suite. On n’a pas vĂ©rifiĂ© si le PGCD Ă©tait bien Ă©gal Ă  1, ce qui est immĂ©diat dans un corps, on n’a pas non plus vĂ©rifiĂ© si a==0.

Le corps fini utilisĂ© dans les codes QR n’a pas cette structure, trop simple, qu’on verra dans la partie suivante : un corps fini est un espace vectoriel de dimension finie sur un corps fini premier, tout comme ℂ est un espace vectoriel de dimension 2 sur ℝ (qui n’est cependant pas un corps premier). Voyons comment construire un corps â„€/pâ„€ en crĂ©ant une classe munie de toutes les mĂ©thodes adĂ©quates.

2. Construction effective de â„€/pâ„€

On va construire pour commencer l’anneau â„€/pâ„€ dans le cas oĂč p est un nombre premier, c’est-Ă -dire le corps â„€/pâ„€, en Python.

2.1 MĂ©morisation

Mon second code, naĂŻf, utilise une fonction qui retourne une classe Zn qui dĂ©pend du paramĂštre p (la classe est donc paramĂ©trĂ©e). La construction fonctionne Ă  peu prĂšs mais coince quand il faut commencer Ă  vĂ©rifier les types dans les classes emboĂźtĂ©es. J’ai donc utilisĂ© le code de Jeremy Kun [5] qui ressemble au mien [2], aux dĂ©corateurs prĂȘts.

Un dĂ©corateur permet de modifier une fonction sans utiliser une batterie de tests (regardez mon deuxiĂšme code oĂč je passe beaucoup de temps Ă  vĂ©rifier finement les types). Le code de Jeremy [6] est certes puissant mais j’ai dĂ» le modifier ponctuellement, notamment pour l’opĂ©ration puissance ou la division euclidienne (sans intĂ©rĂȘt dans un corps). Le voici en version francisĂ©e, dans qrcodeoutils.py :

def memorise(f):
   cache=dict()
 

   def fonctionmemorisee(*args):
      if args not in cache:
         cache[args]=f(*args)
      return cache[args]
   

   fonctionmemorisee.cache=cache

   return fonctionmemorisee

 

if __name__=="__main__":

   @memorise

   def f(x):

      print(x,x*x,"premiĂšre passe")

      return x*x

 

   print(f(2))

   print(f(3))

   print(f(2))

   print(f.cache)

   print(f)

Voici la sortie de ce code :

> ./memorisation.py

2 4 premiĂšre passe

4

3 9 premiĂšre passe

9

4

{(2,): 4, (3,): 9}

<function memorise.<locals>.fonctionmemorisee at 0xb729c6ec>

On teste si la valeur est mĂ©morisĂ©e. Sinon, on la stocke dans le cache qui est un simple dictionnaire. Évidemment, ce code stocke absolument tout sans contrĂŽle. Dans un code qui tourne indĂ©finiment, il faudrait au minimum contrĂŽler la taille du cache.

2.2 Construction de la classe modulo p

La fonction defzn dĂ©finit une classe qui dĂ©pend du paramĂštre p qui est un nombre entier positif. Elle est mĂ©morisĂ©e donc deux classes de mĂȘmes paramĂštres p sont en fait identiques et non deux classes distinctes qui font nĂ©anmoins la mĂȘme chose.

01: @memorise
02: def defzn(p):
03:    class Zn():
04:    def __init__(self,n):
05:    self.n=n%p
06:    self.corps=Zn

Le constructeur d’un Ă©lĂ©ment de la classe le dĂ©finit par ses propriĂ©tĂ©s : sa valeur et le corps auquel il appartient.

08:   def __add__(self,m):
09:      return Zn(self.n+m.n)
10:   def __sub__(self,m):
11:      return Zn(self.n-m.n)
12:   def __mul__(self,m):

13:      return Zn(self.n*m.n)

On dĂ©finit l’addition, la soustraction et la multiplication dans â„€/pâ„€ qui permettent d’utiliser les opĂ©rateurs binaires +, − et *.

14:   def __truediv__(self,m):
15:      return self*m._inverse()

La division (/ en Python 3 et non // qui est la division euclidienne que nous utiliserons plus tard mais pas dans un corps, et qui est dĂ©finie par la mĂ©thode __floordiv__) nĂ©cessite l’inverse modulo p puisque diviser par un nombre est Ă©quivalent Ă  multiplier par son inverse (comme on l’apprend au collĂšge) soit Formule_14, ∀a∈℀/pâ„€, ∀b∈℀/pâ„€*, la fonction _inverse est dĂ©finie plus loin.

16:   def __neg__(self):
17:      return Zn(-self.n)
18:   def __eq__(self,m):
19:      return isinstance(m,Zn) and self.n==m.n

La nĂ©gation unaire (autrement dit l’opposĂ©) est dĂ©finie ainsi que le test d’égalitĂ©. Remarquez que les comparaisons < ou â©Ÿ n’ont aucun intĂ©rĂȘt.

20:   def __pow__(self,exp):
21:   if exp>0:
22:      prod=1
23:      x=self.n
24:      while exp>1:
25:         if exp%2:
26:            prod*=x
27:         x*=x
28:         x%=p
29:         exp//=2
30:      return Zn(prod*x)
31:   elif exp==0:
32:      return Zn(1)
33:   else:

34:      return self._inverse()**-exp

Il s’agit de l’opĂ©rateur de puissance ** y compris si l’exposant est nĂ©gatif. Dans ce cas, on calcule la puissance positive de l’inverse. L’exponentiation rapide revient Ă  dĂ©composer l’exposant en base 2, c’est l’idĂ©e de la multiplication pratiquĂ©e en Égypte antique et encore aujourd’hui en Russie.

Par exemple si on doit calculer a45=a1a4a8a32, la suite des divisions de 45 par les puissances de 2 donne :

divisions

45=2×22+1

22=11×2+0

11=5×2+1

5=2×2+1

2=2×1+0

1=1

reste

1

0

1

1

0

1

prod

a1

a1

a1a4=a5

a5a8=a13

a13

a13a32=a45

x

a1

a2

a4

a8

a16

a32

35:  def __str__(self):
36:  return str(self.n)+"\u0305"
37:  def __repr__(self):
38:  return "%d [mod %d]"%(self.n,self.p)

La premiĂšre fonction est utilisĂ©e par les fonctions internes str et print, la deuxiĂšme quand on affiche une liste d’élĂ©ments de la classe, voir ci-aprĂšs.

39:  def _inverse(self):

40:  i,_=bezout(p,self.n)

41:  return Zn(i)

Dans la fonction qui calcule l’inverse, on ne vĂ©rifie pas si l’élĂ©ment est bien inversible, comme on travaille dans un corps fini, on suppose que tout va bien. Au cas oĂč on diviserait quand mĂȘme par 0, une exception sera levĂ©e dans la boucle dans la fonction bezout lors de la division euclidienne (ligne 04 du code source de bezout, voir plus haut).

42:  Zn.p=p
43:  Zn.__name__ ="â„€/%dâ„€"%p
44:  return Zn

Enfin, on dote la classe elle-mĂȘme des deux propriĂ©tĂ©s : son cardinal et son nom et on la retourne.

Jouons un peu avec notre classe :

if __name__=="__main__":

   mod7=defzn(7)

   print([mod7(i) for i in range(10)])

   print(mod7(3)**-2)

   print(mod7(2)*mod7(4)-mod7(5))

   print(mod7(1).corps.__name__)

   print(mod7.__name__)

   z7=defzn(7)

   print(z7==mod7)

On obtient ceci :

[0 [mod 7], 1 [mod 7], 2 [mod 7], 3 [mod 7], 4 [mod 7], 5 [mod 7], 6 [mod 7], 0 [mod 7], 1 [mod 7], 2 [mod 7]]

4̅

3̅

â„€/7â„€

â„€/7â„€

True

Conclusion

Voici ce qu’on peut faire en Python. Le problĂšme est qu’il faut empiler beaucoup de briques par-dessus cette structure alors qu’en fait, on peut accĂ©lĂ©rer le processus. Ce qu'il ne faut pas faire pour corriger un code QR... et ce n'est pas fini !

Références

[1]  PATROIS N., Â« DĂ©coder un code QR Â», GNU/Linux Magazine n°194, p. 32 Ă  41.

[2] Le code et les exemples sont prĂ©sents ici : https://github.com/GLMF/GLMF194.

[3] LANGROGNET F., « Peut-on vraiment calculer avec un ordinateur : les opĂ©rations Â», GNU/Linux Magazine n°194, p. 22 Ă  31.

[4] L’algorithme de Blankinship est prĂ©sentĂ© ici : http://www.les-mathematiques.net/phorum/read.php?5,516246.

[5] http://jeremykun.com/2014/03/13/programming-with-finite-fields/. J’ai francisĂ© le code et je lui ai ajoutĂ© et supprimĂ© ce qui me semble ou non pertinent mathĂ©matiquement (par exemple, la division euclidienne ou la valeur absolue n’ont aucun intĂ©rĂȘt dans un corps fini).

[6] http://jeremykun.com/2012/03/22/caching-and-memoization/.



Article rédigé par

Par le(s) mĂȘme(s) auteur(s)

NouveauLes derniers articles Premiums

Nouveau Les derniers articles Premium

Bun.js : l’alternative Ă  Node.js pour un dĂ©veloppement plus rapide

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Dans l’univers du dĂ©veloppement backend, Node.js domine depuis plus de dix ans. Mais un nouveau concurrent fait de plus en plus parler de lui, il s’agit de Bun.js. Ce runtime se distingue par ses performances amĂ©liorĂ©es, sa grande simplicitĂ© et une expĂ©rience dĂ©veloppeur repensĂ©e. Peut-il rivaliser avec Node.js et changer les standards du dĂ©veloppement JavaScript ?

PostgreSQL au centre de votre SI avec PostgREST

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Dans un systĂšme d’information, il devient de plus en plus important d’avoir la possibilitĂ© d’échanger des donnĂ©es entre applications. Ce passage au stade de l’interopĂ©rabilitĂ© est gĂ©nĂ©ralement confiĂ© Ă  des services web autorisant la mise en Ɠuvre d’un couplage faible entre composants. C’est justement ce que permet de faire PostgREST pour les bases de donnĂ©es PostgreSQL.

La place de l’Intelligence Artificielle dans les entreprises

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’intelligence artificielle est en train de redĂ©finir le paysage professionnel. De l’automatisation des tĂąches rĂ©pĂ©titives Ă  la cybersĂ©curitĂ©, en passant par l’analyse des donnĂ©es, l’IA s’immisce dans tous les aspects de l’entreprise moderne. Toutefois, cette rĂ©volution technologique soulĂšve des questions Ă©thiques et sociĂ©tales, notamment sur l’avenir des emplois. Cet article se penche sur l’évolution de l’IA, ses applications variĂ©es, et les enjeux qu’elle engendre dans le monde du travail.

Petit guide d’outils open source pour le tĂ©lĂ©travail

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Ah le Covid ! Si en cette pĂ©riode de nombreux cas resurgissent, ce n’est rien comparĂ© aux vagues que nous avons connues en 2020 et 2021. Ce flĂ©au a contraint une large partie de la population Ă  faire ce que tout le monde connaĂźt sous le nom de tĂ©lĂ©travail. Nous avons dĂ» changer nos habitudes et avons dĂ» apprendre Ă  utiliser de nombreux outils collaboratifs, de visioconfĂ©rence, etc., dont tout le monde n’était pas habituĂ©. Dans cet article, nous passons en revue quelques outils open source utiles pour le travail Ă  la maison. En effet, pour les adeptes du costume en haut et du pyjama en bas, la communautĂ© open source s’est dĂ©menĂ©e pour proposer des alternatives aux outils propriĂ©taires et payants.

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous dĂ©sirez apprendre le langage Python, mais ne savez pas trop par oĂč commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en dĂ©couvrant l'Ă©cosystĂšme de Python et en Ă©crivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tùche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de possĂ©der des pĂ©taoctets de donnĂ©es si l'on est incapable d'analyser ces derniĂšres ? Cette liste vous aidera Ă  "faire parler" vos donnĂ©es.
Voir les 67 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous