Dans les discussions précédentes nous avons supposé l'existence d'une fonction H qui fasse correspondre, avec toutes les exigences nécessaires (rapidité, uniformité, etc.), au domaine de valeurs des clés K celui des adresses [0 .. M-1] du tableau de h-code. La question est alors : comment construire (ou choisir) une telle fonction H ?
De toutes les formes de fonctions H (cf. Knuth, vol. III déjà cité) nous n'en retiendrons que 2 :
Le papier suivant constitue une excellente synthèse sur les fonctions de h-code et contient une bibliographie très étendue sur ce sujet :
G.D. Knott, "Hashing functions".
The Computer Journal, vol. 18, No. 3, 1975, pp. 265-278.
La méthode de division pour une fonction de h-code est celle que nous avons employée dans les exemples traités jusqu'ici. Elle consiste à prendre comme indice dans le tableau le reste de la division de la clé (il faut qu'elle soit entière !) par la taille du tableau.
c'est-à-dire H(k) = k mod M (1)
Il est toujours possible, par un procédé adéquat, de transformer une clé quelconque en une valeur entière, ne serait-ce qu'en déterminant son ordinal dans un ensemble de valeurs possibles.
Dans la formule (1) il faut cependant choisir M avec soin. Ainsi, si M est une puissance de la base numérique de la machine, M = 2r, alors le calcul de H(k) revient à isoler les r bits les moins significatifs de la clé K (par divisions entières successives dont on ne considère que le reste).
Prendre M parmi les nombres premiers est un pas dans la bonne direction, cependant il faut alors faire attention au fait que dans certains cas le calcul de H(k) revient à simplement superposer les valeurs des composantes d'une clé. Par exemple :
Si pour la fonction de h-code on considère
H(C1 C2 C3) = (C1 C2 C3) mod (212 + 1)
et, si on exprime (C1 C2 C3) comme une combinaison linéaire dans la base 64, on obtient :
H(C1 C2 C3) = (C1 * 642 + C2 * 64 + C3) mod (642 + 1)
alors, si (C3 + 64*C2) > C1 , le quotient de (C1 C2 C3) divisé par 212+1 est simplement C1 et le reste est de la forme :
(C1*642 + C2*64 + C3) - (C1*(642 + 1)) = (C2*64 + C3) - C1
Ainsi exprimée en base 64, la valeur de H(C1 C2 C3) est (C2 C3-C1)64 , qui est une simple superposition des caractères de la clé K. On voit donc que différentes permutations des mêmes lettres donneront le même résultat et augmenteront ainsi le risque de collision.
En généralisant on dira que choisir la taille M d'un tableau de h-code comme étant de la forme
bra,
où b est la base de l'ensemble des caractères et r ainsi que a sont de petites constantes, est à éviter!
Si ce choix est évité, l'expérience montre que la méthode de division présente de bons résultats. De plus la méthode de division semble (expérimentalement) se combiner valablement avec un traitement de collisions par chaînage externe.
Ces méthodes reposent souvent sur les propriétés de distribution de nombres particuliers. Ainsi, si l'on considère le nombre d'or
et que l'on calcule ses multiples i* pour i [1,10], que l'on isole la partie fractionnaire de ces multiples et que l'on détermine le plus grand entier plus petit ou égal à 10 fois la partie fractionnaire, on obtient le tableau suivant (avec {i*} = (i* - i*), où { } signifie partie fractionnaire) :
multiple | partie fract. | plancher de 10*partie fract. |
|
---|---|---|---|
i | i* | {i*} | 10*{i*} |
1 | 0.618034 | .618034 | 6 |
2 | 1.236068 | .236068 | 2 |
3 | 1.854102 | .854102 | 8 |
4 | 2.472136 | .472136 | 4 |
5 | 3.090170 | .090170 | 0 |
6 | 3.708204 | .708204 | 7 |
7 | 4.326238 | .326238 | 3 |
8 | 4.944272 | .944272 | 9 |
9 | 5.562306 | .562306 | 5 |
10 | 6.180340 | .180340 | 1 |
L'examen de ce tableau appelle quelques remarques :
Ces différentes constatations peuvent conduire à la généralisation suivante. Si R est un nombre irrationnel, alors la suite obtenue par :
{R}, {2R}, ... {nR} où {x} indique la partie fractionnaire de x
lorsqu'elle est placée dans l'intervalle [0,1], le subdivise en M + 1 segments de façon identique au nombre d'or .
Ainsi, si l'on a une table de dimension M et une clé entière K qui est utilisée comme multiplicateur de R, on peut construire la fonction de h-code suivante :
H(K) = M * {KR} (2)
La formule (2) est générale et, dans une application réelle, il faut, si possible, une fonction de h-code qui :
Un développement complexe (cf. Knuth) permet de montrer que ces objectifs sont atteints si l'on considère la formule suivante :
étant donné un nombre irrationnel R et un tableau de stockage de taille M=2m, on détermine un entier A tel que R A/w
où w représente le nombre d'entiers représentables sur 1 mot machine de r bits (w = 2r)
on obtient alors :
H(k) = M * ((k* A/w ) mod 1) = (M*(A*k mod w)) / w(k représente la valeur entière de la clé) ce qui revient à multiplier k par A, tronquer le résultat à 1 mot machine et conserver les m bits de gauche ( M=2m).
En conclusion, on peut dire que la méthode du h-code constitue une alternative à la structure d'arbre binaire lorsque :
A noter que si l'on désire pouvoir supprimer des éléments du tableau en cours de traitement, un algorithme de traitement de collisions par chaînage externe est préférable car plus efficace.
Site Hosting: Bronco