TD — Séance 3 — Tables de hachage
Codes, insertions, sondages, suppression et rehash
Appliquer à la main, sur des données nouvelles, les mécanismes du cours : calcul d’un hashCode et de l’indice d’une case, insertion par chaînage séparé et par sondage linéaire, suppression par pierre tombale, rehash, contrat equals / hashCode. Chaque réponse se donne sous la forme demandée (tableau, état de la table, nombre de comparaisons ou de sondages) et se justifie en une ou deux phrases.
Conventions. La compression est celle du cours, (h & 0x7fffffff) % m. Pour une clé entière positive, la méthode Integer.hashCode() rend la valeur de l’entier, et l’indice est \(k\bmod m\). Le chaînage ajoute en queue de liste. Le sondage linéaire examine les cases \(h(k), h(k)+1, \dots\) modulo \(m\) ; chaque case examinée compte pour un sondage. Une recherche en chaînage compte une comparaison par nœud examiné.
Exercice 1 : codes et compressions
Les codes des caractères utiles sont K = 75, a = 97, n = 110, e = 101, L = 76, m = 109, i = 105. Aides : \(31\times75=2\,325\) ; \(31\times2\,422=75\,082\) ; \(31\times75\,192=2\,330\,952\) ; \(2^{31}=2\,147\,483\,648\) ; \(2^{32}=4\,294\,967\,296\).
- Calculer
"Kane".hashCode()par le schéma de Horner ; donner \(h\) après chaque caractère. - En déduire l’indice de
"Kane"dans une table de 16 cases, puis de 10 cases. - Le schéma de Horner appliqué à
"Lamine"sans limite de taille donne \(2\,268\,748\,648\). Montrer que cette valeur ne tient pas dans unintet en déduire"Lamine".hashCode(). - Pour \(m=10\), calculer
h % 10,Math.floorMod(h, 10),(h & 0x7fffffff) % 10etMath.abs(h) % 10. Aides : \(2\,026\,218\,648=10\times202\,621\,864+8\) ; \(2^{31}-2\,026\,218\,648=121\,265\,000\). - Même question pour \(m=16\), sachant que \(2\,026\,218\,648=16\times126\,638\,665+8\). Expliquer pourquoi trois des quatre écritures coïncident ici, et non pour \(m=10\).
- Quelle valeur rend
Math.abs(Integer.MIN_VALUE) % 10? Que se passe-t-il si cette valeur sert d’indice ?
Exercice 2 : une trace de chaînage séparé
Le service de scolarité range sept numéros de dossier dans une table à chaînage séparé de \(m=11\) cases, sans redimensionnement. On insère, dans cet ordre, 31, 52, 17, 74, 9, 40 et 63.
- Calculer les sept indices.
- Donner l’état final de la table (une liste par case, dans l’ordre de la liste).
- Donner, pour chaque clé, le nombre de comparaisons d’une recherche réussie, puis leur moyenne.
- Compter les comparaisons de
get(63),get(85)etget(96). - Calculer \(\alpha\) et comparer la moyenne de (c) à \(1+(n-1)/(2m)\).
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|
Exercice 3 : la même trace par sondage linéaire
On insère les mêmes clés, dans le même ordre, dans une table à sondage linéaire de \(m=11\) cases, sans redimensionnement.
- Compléter le tableau d’insertion ci-dessous.
- Donner l’état final de la table et les clusters (cases occupées consécutives, en tenant compte du retour de la case 10 à la case 0).
- Calculer le nombre moyen de sondages d’une recherche réussie et le comparer à \(S(7/11)\).
- Donner les cases visitées et le nombre de sondages de
get(63)et deget(85); comparer le second à \(U(7/11)\). - Comparer les coûts des recherches de 63 et de 85 à ceux de l’exercice 2.
| clé | \(h\) | cases examinées | case | sondages |
|---|---|---|---|---|
| 31 | ||||
| 52 | ||||
| 17 | ||||
| 74 | ||||
| 9 | ||||
| 40 | ||||
| 63 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|
Exercice 4 : suppression par pierre tombale
On part de la table finale de l’exercice 3. Une pierre tombale (tombstone), notée X, marque une case dont la clé a été supprimée. Les règles sont les suivantes :
remove(k)cherche \(k\) et remplace sa case par X, sans jamais la vider ;get(k)examine les cases comme d’habitude, passe les X sans s’arrêter, et ne s’arrête que sur \(k\) ou sur une case vide ;put(k, v)cherche \(k\) jusqu’à une case vide en retenant le premier X rencontré ; si \(k\) est absente, elle est rangée dans ce X, ou, à défaut, dans la case vide.
- On vide la case de 74 sans précaution. Quelles cases
get(63)visite-t-elle, et que rend-elle ? - On repart de la table de l’exercice 3 et l’on supprime 74 par
remove. Donner l’état de la table. - Donner les cases visitées et le nombre de sondages de
get(63), puis deget(85). - On exécute
put(96, …), puisput(41, …). Pour chacune, donner les cases examinées et la case choisie, puis l’état final. - Une table qui reçoit beaucoup de suppressions accumule des X. Pourquoi faut-il les compter avec les paires pour décider d’un rehash ? Que deviennent-ils pendant le rehash ?
Exercice 5 : rehash
Une table à chaînage séparé compte \(m=4\) cases ; son seuil est \(0{,}75\). On insère, dans cet ordre et en queue de liste, 13, 6, 21, 8, 30, 17 et 2, en doublant \(m\) dès que \(\alpha>0{,}75\) après une insertion.
- Compléter le tableau ci-dessous.
- Donner l’état de la table juste après chaque rehash.
- Combien de copies les rehashs font-ils au total ? Quel est le facteur de charge final ?
- La même table, partant de \(m=4\), reçoit 60 clés distinctes. À quelles insertions double-t-elle ? Donner les valeurs finales de \(m\) et de \(\alpha\), et le nombre total de copies ; établir, pour \(m_0=4\), la borne analogue à la borne \(2N+K-7\) du cours (le rehash numéro \(j\), pour \(j=0,1,\dots,K\), a lieu à l’insertion \(3\cdot2^j+1\)), puis la vérifier.
| clé | case | \(n\) | \(n/m\) avant le test | rehash ? | \(m\) après |
|---|---|---|---|---|---|
| 13 | |||||
| 6 | |||||
| 21 | |||||
| 8 | |||||
| 30 | |||||
| 17 | |||||
| 2 |
Exercice 6 : le contrat equals / hashCode
La scolarité identifie un étudiant par son matricule. Quatre versions de la classe Etudiant sont écrites ; toutes ont deux champs matricule et nom de type String et un constructeur qui les recopie.
- A :
equalscompare les matricules ;hashCoden’est pas redéfini. - B :
equalscompare les matricules ;hashCoderendnom.hashCode(). - C :
equalscompare les matricules ;hashCoderendmatricule.hashCode(). - D :
equalscompare matricule et nom ;hashCoderendObjects.hash(matricule, nom); le champnomn’est pasfinal.
Pour les versions A, B et C, le programme suivant est exécuté (Etudiant désignant la version étudiée) :
Set<Etudiant> inscrits = new HashSet<>();
inscrits.add(new Etudiant("2026-0417", "Ndeye Fall"));
inscrits.add(new Etudiant("2026-0417", "Ndèye Fall"));
System.out.println(inscrits.size());
System.out.println(inscrits.contains(new Etudiant("2026-0417", "N. Fall")));Pour chacune des versions A, B et C, prévoir les deux lignes écrites et dire si le contrat est respecté.
Pour la version B, que rend
inscrits.contains(new Etudiant("2026-0417", "Ndeye Fall"))? Expliquer pourquoi la réponse n’est pas la même qu’avec"N. Fall".Pour la version D, on exécute :
Set<Etudiant> inscrits = new HashSet<>(); Etudiant d = new Etudiant("2026-0417", "Ndeye Fall"); inscrits.add(d); d.nom = "Ndeye Fall Diop"; System.out.println(inscrits.size()); System.out.println(inscrits.contains(d));Prévoir les deux lignes écrites et expliquer. Quelle règle sur les clés cette situation enfreint-elle ?
Écrire, pour la version C, une méthode
equalset une méthodehashCodequi respectent le contrat, en une instruction par ligne.
Exercice 7 : le nombre de cases vides
On range \(n\) clés dans \(m\) cases sous l’hypothèse du hachage uniforme (chaque clé tombe dans chaque case avec la probabilité \(1/m\), indépendamment des autres), par chaînage séparé.
- Montrer qu’une case donnée reste vide avec la probabilité \((1-1/m)^n\), et en déduire le nombre moyen de cases vides.
- Montrer que, pour \(m\) grand, cette probabilité vaut environ \(e^{-\alpha}\).
- Calculer le nombre moyen de cases vides pour \(n=6\,000\) et \(m=8\,000\), par la formule exacte puis par l’approximation. Aide : \(e^{-0{,}75}\approx0{,}4724\).
- Le nombre moyen de cases non vides est-il égal à \(n\) ? Que mesure l’écart entre les deux ?
Pour cette séance
Le cours
Les slides
À lire en ligne
À télécharger
- Code de départ du TP (ZIP, 7 Ko)