web-dev-qa-db-fra.com

Pourquoi utiliser un nombre premier dans hashCode?

Je me demandais simplement pourquoi les nombres premiers sont utilisés dans la méthode hashCode() d'une classe? Par exemple, lorsque vous utilisez Eclipse pour générer ma méthode hashCode(), il existe toujours le nombre premier 31 utilisé:

public int hashCode() {
     final int prime = 31;
     //...
}

Références:

Voici un bon aperçu du Hashcode et un article sur le fonctionnement du hachage trouvé (C # mais les concepts sont transférables): Directives et règles d'Eric Lippert pour GetHashCode ()

157
Ian Dallas

Parce que vous voulez que le nombre que vous multipliez et le nombre de compartiments dans lesquels vous insérez aient des factorisations premières orthogonales.

Supposons qu'il y ait 8 seaux dans lesquels insérer. Si le nombre que vous utilisez pour multiplier par est un multiple de 8, alors le compartiment inséré sera uniquement déterminé par l'entrée la moins significative (celle qui n'est pas du tout multipliée). Des entrées similaires vont entrer en collision. Pas bon pour une fonction de hachage.

31 est un nombre premier assez important pour que le nombre de compartiments ne soit probablement pas divisible par celui-ci (et en fait, les modernes Java maintiennent le nombre de compartiments à une puissance de 2).

93
ILMTitan

Les nombres premiers sont choisis de manière à mieux répartir les données entre les compartiments de hachage. Si la distribution des entrées est aléatoire et régulièrement répartie, le choix du code/module de hachage importe peu. Cela n'a d'impact que lorsqu'il existe un certain modèle pour les entrées.

C'est souvent le cas lorsqu'il s'agit d'emplacements de mémoire. Par exemple, tous les entiers 32 bits sont alignés sur les adresses divisibles par 4. Consultez le tableau ci-dessous pour visualiser les effets de l’utilisation d’un module premier par rapport au module non premier:

Input       Modulo 8    Modulo 7
0           0           0
4           4           4
8           0           1
12          4           5
16          0           2
20          4           6
24          0           3
28          4           0

Notez la distribution presque parfaite lorsque vous utilisez un module premier par rapport à un module non premier.

Cependant, bien que l'exemple ci-dessus soit en grande partie artificiel, le principe général est que, lorsqu'il s'agit d'un modèle d'entrées, l'utilisation d'un module de nombre premier donnera la meilleure distribution.

121
advait

Pour ce que ça vaut, Effective Java 2nd Edition ) renonce à la question des mathématiques et dit simplement que la raison choisir 31 est:

  • Parce que c'est un nombre premier impair et qu'il est "traditionnel" d'utiliser des nombres premiers
  • C'est également un moins qu'une puissance de deux, ce qui permet une optimisation au niveau du bit

Voici la citation complète, tirée de Rubrique 9: Toujours remplacer hashCode lorsque vous remplacez equals:

La valeur 31 a été choisie parce que c'est un nombre impair. S'il était pair et que la multiplication débordait, l'information serait perdue, car la multiplication par 2 équivaut à un décalage. L'avantage d'utiliser un nombre premier est moins clair, mais c'est traditionnel.

Une propriété de Nice de 31 est que la multiplication peut être remplacée par un décalage ( §15.19 ) et une soustraction pour de meilleures performances:

 31 * i == (i << 5) - i

Les machines virtuelles modernes effectuent ce type d'optimisation automatiquement.


Bien que la recette dans cet élément donne des fonctions de hachage raisonnablement bonnes, elle ne fournit pas de fonctions de hachage à la pointe de la technologie, pas plus que Java fournissent les mêmes fonctions de hachage qu'à partir de la version 1.6. Écriture ces fonctions de hachage constituent un sujet de recherche, qu’il convient de laisser aux mathématiciens et aux informaticiens théoriques.

Peut-être qu'une version ultérieure de la plate-forme fournira-t-elle des fonctions de hachage de pointe pour ses classes et des méthodes utilitaires permettant aux programmeurs moyens de créer de telles fonctions de hachage. En attendant, les techniques décrites dans ce point devraient convenir à la plupart des applications.

Plutôt que de façon simpliste, on peut dire que l’utilisation d’un multiplicateur avec de nombreux diviseurs donnera plus collisions de hachage . Comme pour un hachage efficace, nous souhaitons minimiser le nombre de collisions, nous essayons d'utiliser un multiplicateur comportant moins de diviseurs. Un nombre premier, par définition, a exactement deux diviseurs distincts et positifs.

Questions connexes

24
polygenelubricants

J'ai entendu dire que 31 avait été choisi pour que le compilateur puisse optimiser la multiplication afin de décaler à gauche 5 bits, puis soustraire la valeur.

5
Steve Kuo

D'abord, vous calculez la valeur de hachage modulo 2 ^ 32 (la taille d'un int), vous voulez donc quelque chose de relativement premier à 2 ^ 32 (relativement premier signifie qu'il n'y a pas de diviseur commun). N'importe quel nombre impair ferait cela.

Ensuite, pour une table de hachage donnée, l'index est généralement calculé à partir de la valeur de hachage modulo de la taille de la table de hachage. Les tailles des tables de hachage sont souvent choisies comme nombres premiers pour cette raison. Dans le cas de Java, l’implémentation Sun veille à ce que la taille soit toujours égale à une puissance de deux. Un nombre impair suffirait ici aussi. limiter les collisions plus loin.

Le mauvais effet si la table de hachage et le multiplicateur avaient un facteur commun n pourrait être que, dans certaines circonstances, seules les entrées 1/n de la table de hachage seraient utilisées.

2
starblue

Voici un citation un peu plus près de la source.

Cela revient à:

  • 31 est premier, ce qui réduit les collisions
  • 31 produit une bonne distribution, avec
  • un compromis raisonnable en vitesse
2
John

Les nombres premiers sont utilisés pour minimiser les collisions lorsque les données présentent des modèles particuliers.

Tout d’abord: si les données sont aléatoires, il n’ya pas besoin de nombre premier, vous pouvez effectuer une opération de modification contre n’importe quel nombre et vous obtiendrez le même nombre de collisions pour chaque valeur possible du module.

Mais lorsque les données ne sont pas aléatoires, des choses étranges se produisent. Par exemple, considérons des données numériques qui sont toujours un multiple de 10.

Si nous utilisons le mod 4, nous trouvons:

10 mod 4 = 2

20 mod 4 = 0

30 mod 4 = 2

40 mod 4 = 0

50 mod 4 = 2

Donc, sur les 3 valeurs possibles du module (0,1,2,3), seules 0 et 2 auront des collisions, ce qui est mauvais.

Si nous utilisons un nombre premier comme 7:

10 mod 7 = 3

20 mod 7 = 6

30 mod 7 = 2

40 mod 7 = 4

50 mod 7 = 1

etc

Nous notons également que 5 n’est pas un bon choix mais que 5 est premier, car toutes nos clés sont un multiple de 5. Cela signifie que nous devons choisir un nombre premier qui ne divise pas nos clés, le choix d’un grand nombre premier est: généralement assez.

Le fait d’utiliser des nombres premiers a donc pour but de neutraliser l’effet des motifs sur les clés dans la répartition des collisions d’une fonction de hachage.

0
Amar Magar

Cela permet généralement d’obtenir une répartition plus uniforme de vos données parmi les compartiments de hachage, en particulier pour les clés à faible entropie.

0
fennec

31 est également spécifique à Java HashMap qui utilise un type de données int comme hash. Ainsi, la capacité maximale de 2 ^ 32. Il n’ya aucun intérêt à utiliser des nombres premiers de Fermat ou de Mersenne plus grands.

0
DED