Habituellement, une fonction de hachage simple fonctionne en prenant les «composants» de l'entrée (caractères dans le cas d'une chaîne), en les multipliant par les puissances d'une constante et en les ajoutant ensemble dans un type entier. Ainsi, par exemple, un hachage typique (bien que pas particulièrement bon) d'une chaîne pourrait être:
(first char) + k * (second char) + k^2 * (third char) + ...
Ensuite, si un tas de chaînes ayant toutes le même premier caractère sont introduites, alors les résultats seront tous les mêmes modulo k, au moins jusqu'à ce que le type entier déborde.
[Par exemple, la chaîne hashCode de Java est étrangement similaire à cela - elle fait l'ordre inverse des caractères, avec k = 31. Vous obtenez donc des relations de frappe modulo 31 entre des chaînes qui se terminent de la même manière, et des relations de frappe modulo 2 ^ 32 entre des chaînes qui sont les mêmes sauf près de la fin. Cela ne gâche pas sérieusement le comportement de la table de hachage.]
Une table de hachage fonctionne en prenant le module du hachage sur le nombre de seaux.
Il est important dans une table de hachage de ne pas produire de collisions pour les cas probables, car les collisions réduisent l'efficacité de la table de hachage.
Supposons maintenant que quelqu'un mette tout un tas de valeurs dans une table de hachage ayant une relation entre les éléments, comme tous ayant le même premier caractère. Il s'agit d'un modèle d'utilisation assez prévisible, je dirais, donc nous ne voulons pas qu'il produise trop de collisions.
Il s'avère que "en raison de la nature des mathématiques", si la constante utilisée dans le hachage et le nombre de compartiments sont coprimes , les collisions sont minimisées dans certains cas courants. S'ils ne sont pas coprime, il existe alors des relations assez simples entre les entrées pour lesquelles les collisions ne sont pas minimisées. Tous les hachages sont égaux modulo au facteur commun, ce qui signifie qu'ils tomberont tous dans le 1 / nème des seaux qui ont cette valeur modulo le facteur commun. Vous obtenez n fois plus de collisions, où n est le facteur commun. Puisque n est au moins 2, je dirais qu'il est inacceptable qu'un cas d'utilisation assez simple génère au moins deux fois plus de collisions que la normale. Si un utilisateur va diviser notre distribution en seaux, nous voulons que ce soit un accident bizarre, pas une simple utilisation prévisible.
Maintenant, les implémentations de table de hachage n'ont évidemment aucun contrôle sur les éléments qui y sont placés. Ils ne peuvent pas empêcher leur relation. Donc, la chose à faire est de s'assurer que le nombre de constantes et de seaux est coprime. De cette façon, vous ne comptez pas uniquement sur le "dernier" composant pour déterminer le module du godet par rapport à un petit facteur commun. Pour autant que je sache, ils n'ont pas besoin d'être les meilleurs pour y parvenir, juste du coprime.
Mais si la fonction de hachage et la table de hachage sont écrites indépendamment, la table de hachage ne sait pas comment fonctionne la fonction de hachage. Il peut s'agir d'une constante avec de petits facteurs. Si vous êtes chanceux, cela pourrait fonctionner complètement différemment et être non linéaire. Si le hachage est assez bon, alors tout nombre de seaux est très bien. Mais une table de hachage paranoïaque ne peut pas assumer une bonne fonction de hachage, elle doit donc utiliser un nombre premier de compartiments. De même, une fonction de hachage paranoïde devrait utiliser une constante première de grande taille, pour réduire le risque que quelqu'un utilise un certain nombre de compartiments, ce qui se trouve avoir un facteur commun avec la constante.
En pratique, je pense qu'il est assez normal d'utiliser une puissance de 2 comme nombre de godets. Ceci est pratique et évite d'avoir à chercher ou à présélectionner un nombre premier de la bonne ampleur. Vous comptez donc sur la fonction de hachage pour ne pas utiliser de multiplicateurs pairs, ce qui est généralement une hypothèse sûre. Mais vous pouvez toujours obtenir de mauvais comportements de hachage occasionnels basés sur des fonctions de hachage comme celle ci-dessus, et le nombre de compartiments principaux pourrait aider davantage.
Mettre sur le principe que "tout doit être premier" est autant que je sache une condition suffisante mais pas nécessaire pour une bonne distribution sur les tables de hachage. Il permet à chacun d'interagir sans avoir à supposer que les autres ont suivi la même règle.
[Modifier: il existe une autre raison, plus spécialisée, d'utiliser un nombre premier de compartiments, à savoir si vous gérez les collisions avec un sondage linéaire. Ensuite, vous calculez une foulée à partir du code de hachage, et si cette foulée s'avère être un facteur du nombre de compartiments, vous ne pouvez effectuer que des sondes (bucket_count / stride) avant de revenir où vous avez commencé. Le cas que vous voulez éviter le plus est stride = 0, bien sûr, qui doit être spécial, mais pour éviter également que bucket_count / stride soit égal à un petit entier, vous pouvez simplement faire le bucket_count premier et ne vous souciez pas de ce que le foulée est fournie, ce n'est pas 0.]