Les tables de hachage
Pour 47 stocké dans une table de 7 alvéoles, l'ordinateur va directement à l'alvéole 47 mod 7 = 5 au lieu de chercher dans toute la table.
Transformer une clé en position
Jusqu'ici, trouver une valeur voulait dire chercher : jusqu'à n étapes dans un tableau non trié ou une liste chaînée. Pourtant, les tableaux peuvent atteindre n'importe quelle position en une étape. Et si la clé elle-même indiquait la position ? Si tes clés étaient de petits nombres entiers, par exemple des numéros d'élèves de 0 à 99, tu pourrais garder un tableau de 100 cases et ranger l'élève k dans la case k. Les notes du MIT appellent cela un tableau à accès direct. Cela ne marche plus quand les clés possibles sont immenses, comme des numéros de téléphone ou des mots, car on ne peut pas se permettre une case pour chaque clé possible.
Une fonction de hachage résout ce problème en ramenant n'importe quelle clé dans une petite plage de positions, de 0 à m − 1. Une fonction simple pour les nombres entiers est h(k) = k mod m, le reste de la division par m. Le tableau de m cases est la table de hachage, et ses cases sont souvent appelées alvéoles (buckets). Pour stocker 47 avec m = 7, on calcule 47 mod 7 = 5 et on le range dans l'alvéole 5. Pour retrouver 47 plus tard, on refait le même calcul et on ne regarde que dans l'alvéole 5.
Collisions et chaînage
Il y a un problème. Avec 7 alvéoles et plus de 7 clés possibles, certaines clés doivent partager une alvéole : 12, 33 et 47 donnent toutes le reste 5. C'est ce qu'on appelle une collision, et les notes du MIT font remarquer qu'elle est inévitable, d'après le principe des tiroirs. Le remède habituel est le chaînage : chaque alvéole contient une petite liste, une chaîne, de toutes les clés hachées vers elle. Trouver 47 coûte alors 1 étape pour le hachage plus une comparaison pour chaque clé vérifiée dans la chaîne 5. Si 12 et 33 sont devant lui, cela fait 1 + 3 = 4 étapes.
Quelle est la longueur des chaînes ? Si les clés se répartissent uniformément, chaque chaîne contient environ n/m clés, un nombre appelé facteur de charge (taux de remplissage). Garde m à peu près aussi grand que n et les chaînes restent courtes : trouver, insérer et supprimer prennent alors en moyenne un nombre constant d'étapes. Le danger, c'est une mauvaise répartition. Si toutes les clés sont hachées vers la même alvéole, une seule chaîne contient les n clés et la table ne vaut pas mieux qu'une liste chaînée. Dans le labo, choisis 7 alvéoles et insère 14, 21 et 28 : elles atterrissent toutes dans l'alvéole 0.
Fais glisser pour tourner. Fais défiler ou pince pour zoomer.
Les tables de hachage en JavaScript
On écrit rarement une table de hachage soi-même, car JavaScript dispose de Map et de Set. La spécification ECMAScript dit qu'ils « doivent être implémentés à l'aide de tables de hachage ou d'autres mécanismes qui, en moyenne, offrent des temps d'accès sous-linéaires par rapport au nombre d'éléments ». Autrement dit, map.get(key) et set.has(x) doivent être plus rapides qu'une recherche dans toute la collection. MDN ajoute qu'une Map garde les clés dans l'ordre d'insertion et, contrairement à un objet ordinaire, ne contient aucune clé par défaut qui pourrait entrer en conflit avec les tiennes.
const price = new Map();
price.set("apple", 1.2);
price.set("bread", 2.5);
console.log(price.get("bread")); // 2.5, sans parcourir la liste
console.log(price.has("milk")); // false
const seen = new Set([4, 8, 15]);
console.log(seen.has(8)); // true| Fonction de hachage | Transforme une clé en numéro d'alvéole de 0 à m − 1, par exemple k mod m. |
| Collision | Deux clés différentes reçoivent la même alvéole. Inévitable quand il y a plus de clés que d'alvéoles. |
| Chaînage | Chaque alvéole garde la liste de toutes les clés hachées vers elle. |
| Facteur de charge | n/m, la longueur moyenne d'une chaîne. |
| Map et Set | Les collections à clés intégrées de JavaScript, qui doivent être plus rapides qu'une recherche linéaire en moyenne. |
- Avec h(k) = k mod 7, dans quelle alvéole va 47 ?
- La table contient 12, 25, 33, 47, 70 (insérés dans cet ordre) avec m = 7. Combien d'étapes demande la recherche de 47 (1 pour le hachage plus les clés comparées) ?
- Même table : combien d'étapes pour trouver 12 ?
- Même table : 19 n'est pas stocké. Combien d'étapes demande une recherche de 19 ?
- Même table : quel est le facteur de charge n/m ? (deux décimales)
- Qu'est-ce qu'une collision ?
- Une clé trop grande
- Deux clés différentes hachées vers la même alvéole
- Supprimer une clé deux fois
- Une table sans clés
- Pourquoi aucune fonction de hachage ne peut-elle éviter les collisions quand il y a plus de clés possibles que d'alvéoles ?
- Les ordinateurs arrondissent les nombres
- Le principe des tiroirs
- Les clés sont toujours paires
- Les chaînes sont trop courtes
- Si toutes les clés atterrissent dans la même alvéole, comment se comporte la recherche d'une clé ?
- Toujours 1 étape
- Comme la recherche dans une liste chaînée de n clés
- Elle devient impossible
- Comme la recherche dichotomique
- Qu'exige la spécification ECMAScript pour les temps d'accès d'une Map ?
- Exactement 1 étape
- Sous-linéaires par rapport au nombre d'éléments, en moyenne
- Θ(n log n)
- Rien
- Le hachage de Luhn de 1953 additionnait les paires de chiffres voisins et gardait le dernier chiffre de chaque somme. Que devient 31415926 ?
- Une fonction de hachage transforme une clé en numéro d'alvéole ; k mod m en est une simple.
- Les collisions sont inévitables ; le chaînage garde une liste dans chaque alvéole.
- Avec un facteur de charge n/m maintenu faible, chercher, insérer et supprimer prennent en moyenne un nombre constant d'étapes.
- Map et Set de JavaScript doivent être plus rapides qu'une recherche linéaire en moyenne.
Sources de ce cours (11)
- MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
- lecture notes 2 Data Structures (interface vs data structure, Sequence and Set interfaces, stack = insert_last/delete_last, queue = insert_last/delete_first, array, linked list, dynamic array, amortized Θ(1) append)
- 4 Hashing (chaining, pigeonhole collisions, chain length n/m)
- 6 Binary Trees Part 1 (height, traversal order, O(h) operations) and 7 Binary Trees Part 2: AVL (Adelson-Velsky and Landis, 1962) at ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes
- MDN Web Docs, Array.prototype.shift (shifts all values left by 1)
- Array.prototype.unshift, Map (insertion order, Map vs Object, spec requires sublinear average access) and Set (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)
- ECMAScript 2025 Language Specification (tc39.es/ecma262)
- 24.1 Map Objects and 24.2 Set Objects ("must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements")
- Array.prototype.shift steps (Repeat, while k < len: move element k to k - 1)
- D. E. Knuth, The Art of Computer Programming vol. 3 (2nd ed. 1998) section 6.4 History (H. P. Luhn, IBM internal memorandum January 1953, hashing with chaining; digit pairs added mod 10, 31415926 -> 4548) via gwern.net copy, and IEEE Spectrum, "Hans Peter Luhn and the Birth of the Hashing Algorithm" (2018)
- "A brief history of the stack", SIGCIS (sigcis.org/files/A brief history.pdf: Bauer and Samelson applied for the patent in 1957, US 3,047,228 granted July 31, 1962; IPL list processing by Newell, Simon and Shaw at RAND 1956) and Wikipedia, Information Processing Language (Newell, Shaw, Simon, RAND and Carnegie Institute of Technology, about 1956).
Consulté le: 2026-10-18