Écrire à la main une table de hachage à seaux
Exercice supplémentaire · niveau 3 (difficile) · NSI (première), chapitre 5 — Les dictionnaires · Sous le capot : le hachage
Énoncé
Écrire à la main une table de hachage à seaux : un tableau de listes, où la clé désigne le seau par hash(cle) % nb_seaux. Y ranger clés dans seaux et mesurer la longueur du plus long. Recommencer avec entiers dans seaux.
Corrigé
class TablePauvre:
"""Table de hachage a chainage, ecrite a la main pour VOIR les collisions."""
def __init__(self, nb_seaux=8):
self.seaux = [[] for _ in range(nb_seaux)]
def _indice(self, cle):
return hash(cle) % len(self.seaux)
def ajouter(self, cle, valeur):
seau = self.seaux[self._indice(cle)]
for i, (c, _) in enumerate(seau):
if c == cle:
seau[i] = (cle, valeur) # la cle existait : on REMPLACE
return
seau.append((cle, valeur))
def obtenir(self, cle):
for c, v in self.seaux[self._indice(cle)]:
if c == cle:
return v
raise KeyError(cle)
def plus_long_seau(self):
return max(len(s) for s in self.seaux)
Le chaînage, et ce qu'il coûte. Deux clés différentes peuvent tomber dans le même seau — c'est une collision, et elle est inévitable dès qu'il y a plus de clés que de seaux. La parade est ici la plus simple : on empile les couples dans une liste, et la recherche la parcourt. Le coût d'un accès n'est donc pas vraiment constant : il est constant tant que les seaux restent courts. C'est la nuance que la classe de terminale approfondira, et que la première retient sous la forme « l'accès par clé est direct ».
Les mesures. clés dans seaux : le plus long seau contient éléments — et ce nombre change d'une exécution à l'autre, puisque le hachage des chaînes est tiré au démarrage. Trois exécutions successives donnent les répartitions [4, 4, 4, 1, 6, 2, 2, 1], [3, 5, 2, 1, 2, 3, 7, 1], [4, 3, 1, 2, 3, 3, 1, 7] : jamais trois par seau, comme un partage régulier le voudrait. Le principe des tiroirs garantit seulement qu'un seau en a au moins trois.
Avec entiers dans seaux, en revanche, le plus long seau contient élément : aucune collision. C'est que hash(n) == n pour un petit entier, et que parcourt exactement tous les seaux — un cas idéal, et trompeur. Il illustre bien pourquoi une fonction de hachage se juge sur des données réalistes et non sur celles qui l'arrangent.
Vérification.
t = TablePauvre(8)
for i in range(24):
t.ajouter("cle%d" % i, i)
assert t.obtenir("cle7") == 7
t.ajouter("cle7", 700)
assert t.obtenir("cle7") == 700 # remplacement, pas ajout
assert t.plus_long_seau() >= 3 # principe des tiroirs : 24 clefs, 8 seaux
try:
t.obtenir("absente")
assert False
except KeyError:
pass
L'assert sur plus_long_seau() >= 3 est le seul qu'on puisse écrire : toute valeur exacte serait fausse à la prochaine exécution.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.