David Rivreau http://ima.uco.fr Table de hachage David Rivreau http://ima.uco.fr
Préambule On souhaite gérer des fiches d’assurés sociaux contenant : Le numéro de sécurité sociale (clé) Le nom Le prénom Le code postal de la ville de résidence Le répertoire des fiches doit permettre : D’ajouter une fiche De retrouver une fiche par sa clé De supprimer une fiche selon le numéro de clé
Préambule Si on utilise un tableau non trié : L’ajout coûte O(1) La recherche et la suppression coûtent O(n) Si on utilise une liste chaînée non triée : L’ajout et la suppressions coûtent O(1) La recherche coûte O(n) Si on utilise un arbre binaire de recherche : L’ajout, la recherche et la suppression coûtent O(log n)
Préambule Idéalement, on pourrait imaginer stocker chaque fiche dans une table T indicée par les clés : Ajout en position T[clé] coût O(1) Recherche en position T[clé] coût O(1) Suppression en position T[clé] coût O(1) Problème : le nombre de clés possibles est énorme Il faudrait un espace mémoire immense pour stocker relativement peu de données Impraticable directement en pratique
Idée : hachage Notations Généralement, on a : |U| >> |K| U = univers des clés possibles K = clés effectivement utilisées Généralement, on a : |U| >> |K| Principe On va utiliser une fonction h qui transforme toute clé en une valeur entière sur [0..n[, avec n << |U| On stocke la fiche sur l’emplacement désigné par T[h(clé)]
Illustration du hachage 1 U : univers des clés 2 … h(k2) k2 , Durand, … … … K : clés possibles h(k4) k4 , Martin … k2 h(k3) k3 , Dupont, … … k4 h(k1) k3 k1 , Simon, … k1 n-1
Problème de collision La fonction h doit associer une valeur sur [0..n[ pour toute clé de U Comme |U|>> n, on peut avoir pour deux clés distinctes k1 et k2 de K avec l’égalité : h(k1) = h(k2) Ce type de configuration est appelée collision Il y a plusieurs stratégie pour remédier à ce genre de situation : nous allons présenter la solution du chaînage
Résolution des collisions par chaînage 1 U : univers des clés 2 Collision h(k2)= h(k5) … h(k2)= h(k5) k2 , Durand, … k5 , Petit … … … K : clés possibles h(k4) k4 , Martin … k2 h(k3) k5 k3 , Dupont, … … k4 h(k1) k3 k1 , Simon, … k1 n-1 Pour minimiser le risque de collision, on choisit généralement n ≈ 2x|K|
Fonction de hachage clé La fonction de hachage transforme une clé en une valeur entière (hash code) qui correspond à un indice dans la table Idéalement, la fonction doit : Être calculable rapidement Bien distribuer les valeurs (« aléatoirement ») entre 0 et n-1 Indices dans la table 1 clé hachage 2 … n-1
Retour sur les clés Pour plus de simplicité, on suppose généralement que les clés peuvent être interprétées comme des entiers Exemple des chaines de caractères Pour une chaîne de caractères, on peut imaginer sommer les codes des caractères, mais dans ce cas les anagrammes comme « chien », « niche », « chine » sont en collision En Java, on calcule le hash code de la chaîne s par : s0 x 31(n-1) + s1 x 31(n-2) + … + sn-1 où si désigne le ième caractère de la chaîne s
Exemple de fonction de hachage Méthodes de la division On choisit un nombre premier n tel que n ≈ 2x|K| et tel que n n’est pas proche d’une puissance de 2 Fonction h(k) = k % n Justification Un nombre premier va distribuer plus uniformément Si n vaut 2p, la valeur de la fonction de hachage ne va dépendre que des p bits inférieurs de k
Complexité Soit α = |K|/n le facteur de remplissage de la table Résultat Sous l’hypothèse d’un hachage uniforme des clés, la complexité des opérations d’accès, d’insertion et de suppression dans une table de hachage avec chaînage est de O(1 + α)
Application On veut stocker environ 50000 valeurs entières dans un ensemble Implémenter une classe Java permettant d’insérer, de chercher et de supprimer ces valeurs au moyen d’une table de hachage On prend n=100003 On effectue un hachage par division La gestion des collisions est effectuée par chaînage