Les codes correcteurs d'erreurs
Lorsque les données transistent, celles-ci peuvent-être modifiées en subissant des perturbations. En effet, celles-ci sont transmises sous forme d'un signal (onde) qui, s'il rencontre des obstacles (murs, autre onde, ...), peut être altéré. C'est par exemple ce qu'il se passe lorsque l'on est au téléphone et que l'on entend des grésillements. Il est donc nécessaire de pouvoir détecter et corriger ces erreurs afin de retransmettre le signal original. C'est le rôle des codes correcteurs d'erreurs, et c'est grâce à eux que l'on peut tenir une conversation au téléphone sans entendre de grésillements.
Introduction : petit tour de magie
Pour illustrer comment fonctionne un code correcteur, faisons le petit jeu suivant : choisis un personnage parmi ceux qui se trouvent ci-dessous
et réponds aux questions.
Tu as le droit de mentir une fois, pas plus (cela introduira donc une erreur que l'on va corriger) !
Lorsque tu as terminé, clique sur le bouton "Fini!". Le programme te diras alors quel est ton personnage.
Ton personnage est :
...
Explication
Le but est de retrouver un personnage parmi les $16 = 2^4$. Si on les numérote, cela revient à retrouver un nombre entre 0 et 15...
Notons $n$ ce nombre à trouver. En base 2, il s'écrit : $n = a_3a_2a_1a_0$, avec les $a_i \in \{0, 1\}.$
-
Il y a donc 4 "digits" à trouver et seulement 4 questions suffisent si on n'a pas menti (si l'on répond "oui", alors on sait que le digit correspondant vaut 1, 0 sinon) :
- La première sert à trouver $a_0$ et revient à demander si $n$ est impair (tous les nombres impairs ont des lunettes, les autres non)
- La seconde sert à trouver $a_1$ et revient à demander si $n$ est congru à 2 ou 3 modulo 4 (2, 3, 6, 7, 10, 11, 14 et 15 : tous ces nombres ont une chemise, les autres non)
- La troisième sert à trouver $a_2$ et revient à demander si $n$ est congru à 4, 5, 6 ou 7 modulo 8 (4, 5, 6, 7, 12, 13, 14 et 15 : tous ces nombres ont une moustache, les autres non)
- La quatrième sert à trouver $a_3$ et revient à demander si $n$ est supérieur ou égal à 8 (tous ces nombres ont des cheveux, les autres non)
Les autres questions servent à détecter la présence d'une éventuelle erreur.
-
Pour celà, on rajoute 3 caractérisques que l'on répartit sur les personnages de sorte que le nombre minimal de différences entre 2 personnages différents soit 3.
- est à une distance de 1 du personnage que l'on a choisi,
- est à une distance au moins 2 de tous les autres, où la distance désigne ici le nombre de caractérisques qui diffèrent.
Ainsi, lorsqu'on ment une fois, on crée un personnage fictif qui :
- Il suffit donc de trouver le personnage le plus proche du personnage fictif que l'on a crée.
-
En revanche, si on ment 2 fois, on crée un personnage fictif qui :
- est à une distance de 2 du personnage que l'on a choisi,
- est à une distance de 1 d'un autre personnage.
- On ne peut donc pas retrouver le personnage que l'on a choisi au départ si on ment 2 fois, mais on sait que l'on a menti.
Le fait de rajouter de l'information et de les répartir pour que chacun des "mots du code" (ici les personnages) soient à une distance minimale fixée (aient un nombre mininal de différences fixé) est typique des codes correcteurs d'erreurs. Le code décrit plus haut s'appelle le code de Hamming. Il a une distance minimale de 3, il peut donc détecter 2 erreurs (mensonges : c'est à dire que l'on peut savoir si l'on a menti une ou deux fois), et peut en corriger une seule (on ne retrouve le personnage d'origine que si l'on ment une fois, pas plus).
Et les maths dans tout ça ?
Avant d'expliquer concrètement comment fonctionne le code de Hamming, remarquons que si l'on ne ment pas, on cherche à trouver un nombre $n = a_3a_2a_1a_0$ en base 2, c'est à dire
un vecteur $(a_0, a_1, a_2, a_3) \in GF(2)^4$, où $GF(2)$ désigne le corps à 2 éléments, c'est à dire ${\mathbb{Z}/{2\mathbb{Z}}}$, ou encore plus simplement, l'ensemble $\{0, 1\}.$
On souhaite donc, au départ, envoyer à notre interlocuteur un message $v$ qui est en fait un vecteur $(a_0, a_1, a_2, a_3) \in GF(2)^4$.
Pour pouvoir corriger les éventuelles erreurs, on va donc rajouter de la redondance et envoyer un mot de code $m$ obtenu à partir de $v$.
$m$ est en fait un vecteur $GF(2)^7$.
Avant d'aller plus loin, donnons quelques définitions.
-
Un code (noté $C$) est un sous ensemble de $GF(2)^n$ de même cardinal que $GF(2)^k$ (c'est à dire $2^k$). Ainsi, il existe une bijection $f : GF(2)^k \rightarrow C$ qui à un message $v$, associe le mot de code correspondant
$m$.
- message : le vecteur $v$ de $GF(2)^k$ que l'on veut envoyer. Ici, $k = 4$ et un message est un personnage qui a 4 caractéristiques (celles correspondant aux 4 premières questions).
- mot du code : le vecteur $m$ de $GF(2)^n$ que l'on envoie. Ici, $n = 7$ et un mot du code est un personnage à 7 caractéristiques.
- mot : un vecteur $u$ de $GF(2)^n$. L'entier $k$ est appelé la dimension du code et $n$ sa longueur.
On appelle :
Avec ces définitions, on envoie un mot de code $m$ et notre interlocuteur reçoit un mot $m'$. Celui-ci peut avoir subi des perturbations et contenir des erreurs. Le but pour le destinataire est donc de retrouver à partir du mot $m'$ reçu, le mot de code $m$ que l'on a voulu envoyer, et donc le message d'origine $v$ que l'on a voulu envoyer (lorsque l'on a $m$ c'est facile car $v = f^{-1}(m)$).
-
Il y a donc un double enjeu ici :
- comment corriger les erreurs c'est à dire comment retrouver le mot de code $m$ à partir du mot $m'$ reçu ?
- comment générer les mots de code pour qu'ils permettent de facilement corriger les erreurs ? Autrement dit, comment trouver $f$ ?
-
On appelle :
- distance de Hamming : l'application $w : GF(2)^n \times GF(2)^n \rightarrow GF(2)^n$, qui à tout couple de vecteurs $(u, u')$, associe le nombre de coordonnées deux à deux distinctes de $u$ et $u'$. Plus formellement, $w(u, u') = {Card} \{ 1 \leqslant j \leqslant n \;|\; u_j \neq u_j' \}.
- distance minimale du code $C$ (notée $d$): la plus petite distance de Hamming entre deux éléments distincts de $C$.
-
On remarque qu'un code $C$ de distance minimale $d$ peut détecter $d-1$ erreurs (mensonges) et corriger au plus $\frac{d-1}{2}$ erreurs.
Comment générer le code de Hamming ?
-
Un code $C$ est dit linéaire si c'est un sous-espace vectoriel de $GF(2)^n$.
L'application $f$ décrite plus haut est donc une application linéaire bijective et on peut trouver une base
$(e_1, ..., e_k)$ du $GF(2)^n$-espace vectoriel $C$ en prenant les images par $f$ des vecteurs de la base canonique de $GF(2)^k$.
La matrice $G = (e_1, ..., e_k)$ s'appelle la matrice génératrice de code $C$.
Si $d$ est la distance minimale d'un code linéaire $C$, on dit que c'est un code de type $(n, k, d)$.
Cette matrice nous permet donc de répartir les 3 dernières caractérisques de façon à ce que les personnages soient à une distance au moins 3.
Par exemple, supposons que l'on veuille envoyer le numéro 10 (qui s'écrit 1010 en base 2), c'est à dire que l'on veuille au départ envoyer le vecteur $v = (0, 1, 0, 1)$.
Alors, le mot de code $m$ correspondant que l'on va devoir envoyer est $ G \cdot v = G \cdot \left( \begin{array}{c} 0 \\ 1 \\ 0 \\ 1 \\ \end{array} \right) = \left( \begin{array}{c} 0 \\ 1 \\ 0 \\ 1 \\ 1 \\ 0 \\ 0 \\ \end{array} \right) $ (on voit des 0 apparaître car tous les calculs sont fait modulo 2),
c'est pourquoi on a bien rajouté des yeux bleux, mais pas de barbe ni de "dents" à Jenny.
Comment détecter les erreurs ?
Une autre façon de voir $H$ est de considérer $C^{\bot}$ l'orthogonal de C par la forme bilinéaire symétrique non dégénérée définie par :
Pour revenir au code de Hamming, en résolvant le système $HG = 0$ (calcul du noyau), on obtient la matrice de contrôle suivante :
-
Soit $u$ le mot reçu. On calcule son syndrôme $s(u) = Hu$ et on a deux cas :
- $s(u) = 0$ : le mot $u$ est un mot du code et il n'y a pas d'erreur donc $m = u$
- $s(u) \neq 0$ : le mot $u$ a été perturbé et on peut retrouver le mot de code de départ $m$ seulement s'il a subi moins de $t = \frac{d-1}{2}$ erreurs (c'est à dire si $w(u, m) \leqslant t$). En notant $e$ cette erreur, on a $u = m + e$ et, par linéarité de $s$, on a que $s(u) = s(m) + s(e) = s(e) = \sum_{j = 1}^{n}{e_j h_j}$, où $h_j$ désigne le $j$-ième vecteur colonne de $H$. On retrouve donc facilement $e$ et donc $m = v - e$.
Trouver $f$ (ou $G$) : de la magie ?
Pour les plus curieux d'entre vous, vous pourrez vous intéresser aux codes linéaires cycliques de longueur $n$ et à la façon de générer de tels codes. Un thérorème explique qu'un code cyclique linéaire est généré par un polynome unitaire divisant $X^n -1$ dans $GF(2)[X]$.
Dans notre cas, $n=7$ et $X^7-1 = (X+1)(X^3 + X + 1)(X^3 + X^2+1)$ et, le code de Hamming est généré par le polynôme $g(X) = X^3 + X + 1$, ce qui donne la matrice génératrice :