AccueilđŸ‡«đŸ‡·Chercher

Algorithme de Boyer-Moore

En informatique, plus précisément en algorithmique, l'algorithme de Boyer-Moore est un algorithme de recherche de sous-chaßne particuliÚrement efficace, qui est utilisé comme référence avec lequel on compare d'autres algorithmes quand on réalise des expériences de recherche de sous-chaßne[1]. Il a été développé par Robert S. Boyer et J Strother Moore[2] en 1977.

Efficacité / complexité en temps

L'algorithme de Boyer-Moore prĂ©-traite la sous-chaĂźne « clĂ© » (c'est-Ă -dire la chaĂźne recherchĂ©e), et non pas le texte (c'est-Ă -dire la chaĂźne dans laquelle la recherche est effectuĂ©e), Ă  l'inverse de certains algorithmes, qui amortissent le coĂ»t du prĂ©traitement du texte en effectuant de trĂšs nombreuses recherches rĂ©pĂ©titives. Le coĂ»t d'exĂ©cution de l'algorithme de Boyer-Moore peut ĂȘtre sous-linĂ©aire, c'est-Ă -dire qu'il n'a pas besoin de vĂ©rifier chacun des caractĂšres du texte, mais peut au contraire sauter certains d'entre eux. En gĂ©nĂ©ral, l'algorithme devient plus rapide lorsque la longueur de la sous-chaĂźne s'allonge. Cette efficacitĂ© provient du fait que, pour chaque tentative infructueuse de correspondance entre les deux chaĂźnes (texte et sous-chaĂźne), il utilise les informations dĂ©duites de cet Ă©chec pour Ă©liminer le plus grand nombre possible de positions Ă  vĂ©rifier.

En comparaison des mĂ©thodes de recherches basiques par la mĂ©thode de « force brute » (qui recherche la sous-chaĂźne en commençant par chercher partout dans le texte une correspondance du premier caractĂšre de la sous-chaĂźne, puis en vĂ©rifiant si les caractĂšres suivants correspondent) et dont la complexitĂ© en temps (calculĂ©e par exemple selon le nombre de paires de caractĂšres Ă  comparer) croit en O(L · K) oĂč K est la longueur de la sous-chaĂźne clĂ© recherchĂ©e, et L la longueur de la sequence oĂč l’on recherche s’il existe au moins une occurrence de la clĂ©, l’algorithme de Boyer-Moore peut trouver les occurrences en un temps :

  • De l’ordre de O(L / K) dans le cas le plus favorable : dans ce meilleur cas, seul un caractĂšre sur M doit ĂȘtre vĂ©rifiĂ©. Ainsi, plus la sous-chaĂźne est longue, et plus l’algorithme est efficace pour la trouver ;
  • de l’ordre de O(L + K) dans le pire cas (en utilisant la variante amĂ©liorĂ©e de l’algorithme avec la seconde table de vĂ©rification des occurrences).
  • et trĂšs souvent en un temps Ă  peine supĂ©rieur au meilleur cas (qui devient de plus en plus probable quand la longueur K de la clĂ© s’accroĂźt). Le pire cas est obtenu quand le texte contient de trĂšs nombreuses occurrences d’un mĂȘme caractĂšre, et quand la clĂ© cherchĂ©e contient ce caractĂšre frĂ©quent de nombreuses fois en fin de chaine sauf pour le premier caractĂšre qui est diffĂ©rent.

Le cas moyen pour Ă©tablir s’il y a correspondance ou non requiert environ (3 · K) comparaisons. La preuve est due Ă  Richard Cole[3].

Dans le pire cas, les performances de l'algorithme de base (sans la variante avec la seconde table de vĂ©rification des occurrences) pour trouver toutes les correspondances est de l'ordre de O(K · L). Le pire cas se produit quand la sous-chaĂźne consiste en une rĂ©pĂ©tition d’un unique caractĂšre, et que le texte correspond Ă  la rĂ©pĂ©tition de (K - 1) fois ce mĂȘme caractĂšre, prĂ©cĂ©dĂ© par un seul autre caractĂšre diffĂ©rent. Dans ce cas de figure, l’algorithme doit vĂ©rifier (L - K+ 1) dĂ©calages diffĂ©rents dans le texte pour chaque correspondance. Or, chacune de ces vĂ©rifications nĂ©cessite K comparaisons.

En raison de l’intĂ©rĂȘt de la variante avec la seconde table qui permet un comportement sous-linĂ©aire mĂȘme dans le pire cas, cette variante est dĂ©crite ici (et est celle aujourd'hui intĂ©grĂ©e dans de nombreuses bibliothĂšques de traitement du texte pour C/C++, Java, etc.).

Fonctionnement

Principe

L'algorithme de Boyer-Moore est assez surprenant car il effectue la vérification, c'est-à-dire qu'il tente d'établir la correspondance de la sous-chaßne à une certaine position, à l'envers. Par exemple, s'il commence la recherche de la sous-chaßne WIKIPEDIA au début d'un texte, il vérifie d'abord la neuviÚme position en regardant si elle contient un A. Ensuite, s'il a trouvé un A, il vérifie la huitiÚme position pour regarder si elle contient le dernier I de la sous-chaßne, et ainsi de suite jusqu'à ce qu'il ait vérifié la premiÚre position du texte pour y trouver un W.

La raison pour laquelle l'algorithme de Boyer-Moore utilise cette approche à rebours est plus claire si l'on considÚre ce qui se produit quand la vérification échoue, par exemple si au lieu de trouver un A en neuviÚme position, un X est lu. Le X n'apparaßt nulle part dans la sous-chaßne WIKIPEDIA, ce qui signifie qu'aucune correspondance avec la sous-chaßne n'existe au tout début du texte, ainsi que dans les huit positions qui la suivent. AprÚs la vérification d'un seul caractÚre, l'algorithme est capable de passer ces huit caractÚres et de rechercher le début d'une correspondance à partir de la dixiÚme position dans le texte, juste aprÚs le X.

Ce principe de fonctionnement Ă  rebours explique la quelque peu contre-intuitive affirmation disant que plus la sous-chaĂźne est longue, et plus l'algorithme est efficace pour la trouver.

Exemple

Prenons un exemple plus significatif : on veut rechercher les occurrences de la clĂ© de K=6 caractĂšres “string” dans le texte de L=20 caractĂšres “stupid_spring_string".

Avec un algorithme classique utilisant une mĂ©thode naĂŻve, on devrait rechercher le s dans tout le texte sauf les 5 derniers caractĂšres, et donc faire toutes les 15 comparaisons. Et on trouverait 3 occurrences du s au dĂ©but de chaque mot du texte ; pour chacune de ces occurrences on devrait vĂ©rifier la suite de la clĂ© tant qu'elle correspond : on dĂ©tecterait le p une fois pour rejeter l’occurrence commençant par spring, mais le t serait dĂ©tectĂ© 2 fois dans stupid et string ; on doit alors tester la prĂ©sence du r aprĂšs st pour Ă©liminer l’occurrence dans stupid ; il reste encore Ă  vĂ©rifier chacun des 3 autres caractĂšres de la clĂ© string, il faudra donc 23 comparaisons (ce serait pire si le texte contenait de nombreuses occurrences de la quasi-totalitĂ© du dĂ©but de la clĂ©).

Avec l’algorithme de Boyer-Moore, on recherchera aussi les occurrences depuis le dĂ©but du texte (en affichant ci-dessous la clĂ© cherchĂ©e en dessous du texte balayĂ©, les points notĂ©s avant la clĂ© indiquant les positions dĂ©jĂ  Ă©liminĂ©es, le surlignage indique les comparaisons effectuĂ©es), mais on commencera en comparant le dernier caractĂšre de la clĂ© cherchĂ©e :

stupid_spring_string
string

Les deux caractĂšres d et g ne correspondent pas, on a trouvĂ© Ă  la place un d dans le texte, alors qu’il n’y a aucun d dans la clĂ©. On peut sauter directement dans le texte les 6 caractĂšres de la clĂ© :

stupid_spring_string
······string

Le g de la clĂ© ne correspond pas au n du texte. Cependant, la clĂ© contient un n, 1 caractĂšre avant, on peut donc dĂ©caler d’1 position, et vĂ©rifier Ă  nouveau en commençant par la fin de clĂ© :

stupid_spring_string
·······string

On a trouvé successivement la correspondance du g, puis du n, puis du i, puis du r, mais pas du t de la clé. Au lieu de cela on a trouvé un p dans le texte, qui ne figure pas dans la clé, ce qui permet de décaler de 2 caractÚres pour placer le début de la clé aprÚs le p:

stupid_spring_string
·········string

Le g de la clé ne correspond pas au s du texte. Cependant, la clé contient un s, 5 caractÚres avant, on peut donc décaler de 5 positions, et vérifier à nouveau en commençant par la fin de clé :

stupid_spring_string
··············string

On trouve successivement les correspondances du g, puis du n, du i, du r, du t et du s. Il n'y a plus d’autre caractĂšre dans la clĂ©, on a bien trouvĂ© une occurrence en seulement 14 comparaisons (au lieu de 23 avec l’algorithme classique). Si on devait chercher les occurrences suivantes, il suffit de reprendre l’algorithme dans le texte Ă  partir de la position qui suit la position trouvĂ©e.

Contraintes d’implĂ©mentation

Il faut noter que, pour que l’algorithme de Boyer-Moore puisse fonctionner de façon efficace, il est nĂ©cessaire de pouvoir parcourir le texte (ainsi que la clĂ© cherchĂ©e) en le parcourant linĂ©airement en sens inverse, et de pouvoir sauter directement Ă  une position ultĂ©rieure du texte sans avoir Ă  le lire intĂ©gralement entre les deux positions, ni Ă  devoir relire le texte ou la clĂ© depuis le dĂ©but, et sans avoir Ă  utiliser de coĂ»teux tampons mĂ©moire compliquĂ©s Ă  gĂ©rer. Ce n’est pas toujours le cas de tous les flux de fichiers texte Ă  lecture unidirectionnelle.

Et dans le cas oĂč la recherche doit utiliser des comparaisons basĂ©es sur la collation (en) de chaĂźnes conformes Ă  des rĂšgles linguistiques dans lesquelles certaines diffĂ©rences sont ignorĂ©es dans la recherche des correspondances, les Ă©lĂ©ments Ă  comparer ne seront pas les caractĂšres eux-mĂȘmes mais les Ă©lĂ©ments de collation prĂ©calculĂ©s sur la clĂ© et ceux obtenus au fil de l’eau dans le texte, dans lequel il doit ĂȘtre nĂ©cessaire de dĂ©terminer si les positions sont celles marquant la sĂ©paration des Ă©lĂ©ments de collation (afin de ne pas trouver de faux positifs ni oublier des correspondances lorsqu’on saute directement certaines positions ou quand on lit le texte ou la clĂ© en sens inverse) : cela pose certaines difficultĂ©s dans les collations linguistiques comprenant des contractions et expansions, ou encore dans des textes Unicode non normalisĂ©s pour lesquels plusieurs codages sont possibles. Mais des algorithmes complĂ©mentaires ont Ă©tĂ© dĂ©veloppĂ©s pour traiter efficacement cette difficultĂ© (et quelques autres liĂ©es Ă  l’étendue du jeu de caractĂšres Unicode (ou l’étendue numĂ©rique encore plus grande des poids de collation multiniveau)[4].

Pré-traitement

À partir de la clĂ©, l'algorithme prĂ©calcule deux tableaux indiquant un nombre de positions Ă  sauter aprĂšs chaque vĂ©rification dans le texte. Ces tableaux (parfois appelĂ©s « tables de sauts ») exploitent l’information tirĂ©e de la vĂ©rification effectuĂ©e.

  • Le premier se base sur le caractĂšre du texte qui a Ă©tĂ© comparĂ© au dernier caractĂšre de la clĂ© (c’est la premiĂšre comparaison effectuĂ©e puisque les vĂ©rifications se font de droite Ă  gauche).
  • Le second se base sur le nombre de caractĂšres vĂ©rifiĂ©s avec succĂšs (ce nombre peut Ă©galer la taille de la clĂ©, auquel cas la vĂ©rification a rĂ©ussi).

PremiĂšre table de sauts (indicĂ©e par les lettres de l’alphabet)

Le premier tableau utilise le constat suivant : si on cherche par exemple la clé WIKIPEDIA dans le texte ENCYCLOPEDIA, alors la premiÚre vérification sera :

ENCYCLOPEDIA
WIKIPEDIA

Sachant que la premiĂšre lettre du texte qu’on a comparĂ©e est un E, il est inutile de vĂ©rifier les appariements suivants :

ENCYCLOPEDIA
·WIKIPEDIA
ENCYCLOPEDIA
··WIKIPEDIA

On peut sauter directement Ă  celui-ci :

ENCYCLOPEDIA
···WIKIPEDIA

Ici, on a donc avancĂ© de trois positions au lieu d’une seule, c’est-Ă -dire la distance entre la derniĂšre lettre E dans la clĂ© et la fin de la clĂ©. Si cette lettre n’apparaissait pas dans la clĂ©, alors on aurait pu sauter toute la longueur de celle-ci.

La premiĂšre table de sauts est donc facile Ă  calculer : elle associe Ă  chaque caractĂšre de l’alphabet la distance, mesurĂ©e depuis la fin de la clĂ©, de la derniĂšre occurrence de cette lettre dans la clĂ©. La derniĂšre position de la clĂ© n’est pas comptĂ©e dans les occurrences ; autrement, la distance associĂ©e Ă  la derniĂšre lettre de la clĂ© (la lettre A dans l’exemple) serait nulle et on resterait sur place aprĂšs l’avoir lue dans le texte. Les lettres n’apparaissant pas dans la clĂ© sont associĂ©es Ă  la longueur de la clĂ©. Un algorithme de calcul est donc :

  • prĂ©-remplir toutes les valeurs du tableau avec la longueur de la clĂ© ;
  • dĂ©marrer de l’avant-derniĂšre position dans la clĂ© avec le compteur Ă  1, et aller vers le dĂ©but en incrĂ©mentant le compteur ;
  • pour chaque position, si le caractĂšre courant n’est pas dĂ©jĂ  dans le tableau, l’ajouter avec la valeur actuelle du compteur.

Exemple : avec la clĂ© WIKIPEDIA, le premier tableau est rempli comme suit (pour plus de clartĂ©, les entrĂ©es sont donnĂ©es dans l'ordre oĂč elles sont ajoutĂ©es dans le tableau) :

CaractĂšre de l’alphabetDistance Ă  la fin de clĂ©
I1
D2
E3
P4
K6
W8
autres caractĂšres9

Le lecteur attentif remarquera que le A, le dernier caractĂšre de la clĂ©, n’a pas Ă©tĂ© ajoutĂ© dans le tableau. En effet, il ne prĂ©sente pas d’autre occurrence dans la clĂ©.

Notes de performance :

  1. Ce tableau a une taille (nombre total d‘entrĂ©es) indĂ©pendante de la longueur de la clĂ©, mais proportionnelle Ă  la taille de l’alphabet.
    • Si l’alphabet est trĂšs grand (par exemple le rĂ©pertoire universel UCS d’Unicode et ISO/CEI 10646 dont l’alphabet comprend plus d’un million de points de code possibles) sa taille pourrait devenir prohibitive, alors que la plus grande partie de ce tableau contiendrait la valeur par dĂ©faut (la longueur de la clĂ©), et son prĂ©-remplissage peut prendre du temps. Cela peut s’optimiser en remarquant que la table ne contient pas de valeur nulle. la valeur par dĂ©faut pourra donc ĂȘtre codĂ©e par 0 sans ambiguĂŻtĂ©. Cette astuce permet d’économiser l’initialisation du tableau dans un environnement qui prĂ©-remplit les tableaux Ă  zĂ©ro.
    • De plus, l’algorithme de Boyer-Moore doit son efficacitĂ© Ă  sa capacitĂ© de sauter des longueurs de texte suffisantes. Il n’est pas nĂ©cessaire de sauter la distance maximale, une distance raisonnablement grande (par rapport Ă  la longueur de la clĂ©) suffit. Quand l’alphabet est bien plus grand que la clĂ©, l’algorithme restera efficace si on rĂ©duit l’alphabet Ă  des classes de caractĂšres (ou d’élĂ©ments de collation) avec une bonne rĂ©partition.
    • Dans ce cas, le tableau ne sera pas indicĂ© par les caractĂšres mais par les classes de caractĂšres : une fonction de hachage simple rĂ©duisant ce grand alphabet Ă  (par exemple) un ensemble rĂ©duit Ă  256 classes convenablement distribuĂ©es suffira et fonctionnera trĂšs efficacement pour des longueurs de clĂ© pouvant aller jusqu'Ă  plusieurs milliers de caractĂšres, la table permettant alors d’effectuer des sauts de 1 Ă  256 caractĂšres.
  2. En revanche, si l’alphabet est extrĂȘmement rĂ©duit (par exemple, un alphabet binaire), l’efficacitĂ© de l’algorithme sera totalement nulle (par rapport Ă  un algorithme de recherche naĂŻf) avec cette table de sauts. L’astuce consistera Ă  lire le texte et la clĂ© non pas caractĂšre par caractĂšre, mais par groupes de caractĂšres afin d’augmenter l’alphabet Ă  un cardinal suffisant. Par exemple, avec un alphabet binaire, on pourra lire par paquets de 8 caractĂšres, en fixant un caractĂšre de bourrage arbitraire (ou moins probable) pour les caractĂšres (du petit alphabet) qui manquent au dĂ©but de la clĂ© ou au dĂ©but du texte mais qui sont nĂ©cessaires Ă  la formation de groupes complets de lettres convertis en lettres Ă©quivalentes du nouvel alphabet augmentĂ©. On tiendra ensuite compte, lorsque des correspondances sont trouvĂ©es entre des groupes de caractĂšres, du nombre de caractĂšres de bourrage utilisĂ©s pour ajuster la position du groupe trouvĂ©.

Seconde table de saut (indicée par la longueur de la clé comparée avec succÚs)

Le second tableau est sensiblement plus compliquĂ© Ă  calculer : pour chaque valeur de N infĂ©rieure Ă  la longueur K de la sous-chaĂźne clĂ©, il faut calculer le motif composĂ© des N derniers caractĂšres de la sous-chaĂźne K, prĂ©cĂ©dĂ© d'un caractĂšre qui ne correspond pas. Puis, il faut trouver le plus petit nombre de caractĂšres pour lesquels le motif partiel peut ĂȘtre dĂ©calĂ© vers la gauche avant que les deux motifs ne correspondent. Par exemple, pour la sous-chaĂźne clĂ© ANPANMAN longue de 8 caractĂšres, le tableau de 8 lignes est rempli de cette maniĂšre (les motifs dĂ©jĂ  trouvĂ©s dans le texte sont montrĂ©s alignĂ©s dans des colonnes correspondant Ă  l’éventuel motif suivant possible, pour montrer comment s’obtient la valeur de dĂ©calage qui est la seule rĂ©ellement calculĂ©e et stockĂ©e dans la seconde table de saut) :

Indice Motif suivant DĂ©calage
obtenu
ANPANMAN
0 N 1
1 AN 8
2 MAN 3
3 NMAN 6
4 ANMAN 6
5 PANMAN 6
6 NPANMAN 6
7 ANPANMAN 6

Notes relatives à la complexité de calcul cette table :

  • On remarque que cette table contient autant de lignes que la longueur de clĂ©. Si la clĂ© est longue, les valeurs de dĂ©calage dans la table peuvent ĂȘtre elles aussi assez importantes, ce qui va nĂ©cessiter une allocation de mĂ©moire de travail peut-ĂȘtre importante, souvent plus grande en taille elle-mĂȘme que la chaĂźne clĂ© qui utilise un alphabet plus rĂ©duit (par exemple 1 octet par caractĂšre) que les entiers alors qu’en fin de compte les longueurs de clĂ© seront importantes (typiquement des entiers codĂ©s sur 4 octets).
  • La constitution de cette table nĂ©cessite lĂ  aussi de rechercher des sous-chaĂźnes (toutes les sous-chaĂźnes possibles de la fin de clĂ©) pour en trouver pour chacune la derniĂšre occurrence dans la clĂ©, et l’algorithme de Boyer-Moore pourrait ĂȘtre utilisĂ© rĂ©cursivement (mais en utilisant une recherche sur des textes et clĂ©s de direction inversĂ©e).
  • Au-delĂ  d’une certaine longueur raisonnable (par exemple jusqu’aux 256 derniers caractĂšres de la clĂ©), il peut ĂȘtre inutile d’augmenter la taille de cette table puisque le seul but sera de savoir si des dĂ©calages plus grands peuvent ĂȘtre obtenus pour sauter plus vite dans le texte principal. En ne prĂ©-traitant que les 256 derniers caractĂšres en fin de clĂ©, on obtiendra une longueur dĂ©jĂ  suffisante pour accĂ©lĂ©rer la majoritĂ© des recherches (mais si on veut aller au-delĂ , on pourra, lorsque cette table contient dĂ©jĂ  la longueur maximale retenue pour le saut et dans les rares cas oĂč cette longueur serait atteinte, et si l’alphabet est assez discriminant, ce que peut indiquer le taux de remplissage de la premiĂšre table, employer l’algorithme pour rechercher par rĂ©cursion (plutĂŽt que par prĂ©-calcul dans cette table) les sous-chaĂźnes dans la clĂ©.
  • Mais le plus simple est de borner les dĂ©calages Ă  une valeur raisonnable comme 256 et ne pas chercher Ă  aller au-delĂ . Dans ce cas, cette table aura une taille prĂ©dĂ©finie et ne coĂ»tera rien en termes de complexitĂ© pour les clĂ©s trĂšs longues, puisque son remplissage prendra un temps constant.
  • DiffĂ©rentes stratĂ©gies sont possibles selon un compromis espace/temps (des stratĂ©gies dites « avec cache », ne font aucun prĂ©-calcul des tables, mĂȘme si elles en utilisent, mais remplissent cette table Ă  la demande en fonction des longueurs de concordances effectivement trouvĂ©es Ă  rebours lors de la vĂ©rification des occurrences finales dĂ©jĂ  trouvĂ©es par la premiĂšre table ci-dessus).

Variantes

On donne ici le pseudo-code donné dans[5] qui est un autre algorithme que les auteurs appellent aussi algorithme de Boyer-Moore :

entrée : le motif m
sortie : une table T tel que T[j][c] soit le plus grand k avec m[k] = c et k < j
construireTable(m)
      T[0..|m|-1] := table oĂč chaque case est vide
      pour j = 0 Ă  |m| - 1
           T[j] := table oĂč chaque case pour chaque caractĂšre contient -1
           pour k = 0 Ă  j-1
                 T[j][m[k]] = k
      renvoyer T
entrée : un motif t, un texte
sortie : le nombre d'occurrences du motif t dans le texte
BoyerMoore(m, texte)
     T = construireTable(m)
     compteur = 0
     i = 0
     tant que i <=  |texte| - |m|
           k = 0
           pour j = |m| - 1 à 0 en décrémentant de 1 à chaque pas
                  si texte[i+j] != m[j]
                        k = j - T[j][texte[i+j]]
                        sortir de la boucle pour j
           si k = 0
                 occurrence trouvée à la position i
                 compteur = compteur + 1
                 k = 1
           i = i + k
     renvoyer compteur


La fonction construireTable(m) réalise le prétraitement. Elle calcule une table T avec pour tout position j dans le motif m et pour tout caractÚre c, T[j][c] est la plus grande position k dans le motif m avec m[k] = c et k < j. Une fois cette table T construite, l'algorithme de BoyerMoore compte le nombre d'occurrences de m dans le texte. La boucle tant que i parcourt toutes les positions i dans le texte. On parcourt les caractÚres du motif en partant de la droite. En cas de différence ( texte[i+j] != m[j] ), on retient le décalage trouvé dans k et on sort de la boucle (ce décalage vaut au moins 1). Une fois sorti de cette boucle pour interne, on fait face à une occurrence si et seulement si k = 0. Si tel est le cas, on augmente le compteur et on met k à 1. Puis on incrémente i de k.

Voir aussi

Articles connexes

Liens externes

Référence

  1. Andrew Hume et Daniel Sunday, « Fast String Searching », Software: Practice and Experience, vol. 21, no 11,‎ , p. 1221–1248 (DOI 10.1002/spe.4380211105, S2CID 5902579)
  2. Le prĂ©nom de « J Strother Moore » est bien la lettre J et non l’abrĂ©viation « J. » d’un prĂ©nom
  3. Tight bounds on the complexity of the Boyer-Moore algorithm, Proceedings of the 2d Annual ACM-SIAM Symposium on Discrete Algorithms, (1991)
  4. (en) Efficient text searching in Java, par Laura Werner, paru dans Java Report en 1999 (document présenté dans le projet ICU).
  5. Thibaut Balabonski, Sylvain Conchon, Jean-Christophe Filliùtre, Kim Nguyen, Laurent Sartre, Informatique MP2I/MPI: CPGE 1re et 2e années Cours et exercices corrigés, Ellipse, Chapitre 9, Section 9.5.1.1 p. 535-539
Cet article est issu de wikipedia. Text licence: CC BY-SA 4.0, Des conditions supplĂ©mentaires peuvent s’appliquer aux fichiers multimĂ©dias.