web-dev-qa-db-fra.com

Quelles fonctions de hachage entier acceptent une clé de hachage entière?

Quelles fonctions de hachage entier acceptent une clé de hachage entière?

89
Lear

La méthode multiplicative de Knuth:

hash(i)=i*2654435761 mod 2^32

En général, vous devriez choisir un multiplicateur qui correspond à l’ordre de votre taille de hachage (2^32 dans l'exemple) et n'a pas de facteurs communs avec celui-ci. De cette façon, la fonction de hachage couvre tout votre espace de hachage de manière uniforme.

Edit: Le plus gros inconvénient de cette fonction de hachage est qu’elle préserve la divisibilité. Par conséquent, si vos entiers sont tous divisibles par 2 ou par 4 (ce qui n’est pas rare), leurs hachages le seront également. C'est un problème dans les tables de hachage - vous pouvez vous retrouver avec seulement 1/2 ou 1/4 des seaux utilisés.

39
Rafał Dowgird

J'ai trouvé que l'algorithme suivant fournit une très bonne distribution statistique. Chaque bit d'entrée affecte chaque bit de sortie avec une probabilité d'environ 50%. Il n'y a pas de collision (chaque entrée donne une sortie différente). L'algorithme est rapide sauf si la CPU ne dispose pas d'une unité de multiplication entière intégrée. Le code C, en supposant que int est de 32 bits (pour Java, remplacez >> avec >>> et retirez unsigned):

unsigned int hash(unsigned int x) {
    x = ((x >> 16) ^ x) * 0x45d9f3b;
    x = ((x >> 16) ^ x) * 0x45d9f3b;
    x = (x >> 16) ^ x;
    return x;
}

Le nombre magique a été calculé à l'aide d'un programme de test multi-thread spécial qui a duré plusieurs heures et calcule l'effet d'avalanche (le nombre de bits de sortie qui changent si un seul bit d'entrée est modifié; devrait être presque 16 en moyenne), l’indépendance des modifications du bit de sortie (les bits de sortie ne doivent pas dépendre l’un de l’autre) et la probabilité d’une modification de chaque bit de sortie si un bit d’entrée est modifié. Les valeurs calculées sont meilleures que le finaliseur 32 bits utilisé par MurmurHash , et presque aussi bonnes (pas tout à fait) que lors de l'utilisation de AES =. Un léger avantage est que la même constante est utilisée deux fois (cela a légèrement accéléré la dernière fois que j'ai été testé, je ne suis pas sûr que ce soit toujours le cas).

Vous pouvez inverser le processus (obtenir la valeur d'entrée dans le hachage) si vous remplacez le 0x45d9f3b avec 0x119de1f3 (le inverse multiplicatif ):

unsigned int unhash(unsigned int x) {
    x = ((x >> 16) ^ x) * 0x119de1f3;
    x = ((x >> 16) ^ x) * 0x119de1f3;
    x = (x >> 16) ^ x;
    return x;
}

Pour les nombres 64 bits, je suggère d'utiliser ce qui suit, même si ce n'est peut-être pas le plus rapide. Celui-ci est basé sur splitmix64 , qui semble être basé sur l'article de blog Better Bit Mixing (mix 13).

uint64_t hash(uint64_t x) {
    x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
    x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
    x = x ^ (x >> 31);
    return x;
}

Pour Java, utilisez long, ajoutez L à la constante, remplacez >> avec >>> et retirez unsigned. Dans ce cas, l'inversion est plus compliquée:

uint64_t unhash(uint64_t x) {
    x = (x ^ (x >> 31) ^ (x >> 62)) * UINT64_C(0x319642b2d24d8ec3);
    x = (x ^ (x >> 27) ^ (x >> 54)) * UINT64_C(0x96de1b173f119089);
    x = x ^ (x >> 30) ^ (x >> 60);
    return x;
}

Mise à jour: Vous pouvez également consulter le projet prospecteur de fonction de hachage , où d’autres constantes (éventuellement meilleures) sont répertoriées.

126
Thomas Mueller

Cela dépend de la manière dont vos données sont distribuées. Pour un compteur simple, la fonction la plus simple

f(i) = i

ça va être bon (je pense que c'est optimal, mais je ne peux pas le prouver).

26
erikkallen

Cette page répertorie quelques fonctions de hachage simples qui ont tendance à être décentes en général, mais tout hachage simple a des cas pathologiques où il ne fonctionne pas bien.

7
Tyler McHenry
  • Méthode multiplicative 32 bits (très rapide) voir @rafal

    #define hash32(x) ((x)*2654435761)
    #define H_BITS 24 // Hashtable size
    #define H_SHIFT (32-H_BITS)
    unsigned hashtab[1<<H_BITS]  
    .... 
    unsigned slot = hash32(x) >> H_SHIFT
    
  • 32 bits et 64 bits (bonne répartition) à: MurmurHash

  • Fonction de hachage entier
5
bill

Il existe un bon aperçu de certains algorithmes de hachage à l'adresse Eternally Confuzzled . Je recommanderais le hachage un à un de Bob Jenkins, qui atteint rapidement une avalanche et peut donc être utilisé pour une recherche efficace dans la table de hachage.

3
Christoph

La réponse dépend de beaucoup de choses comme:

  • Où comptez-vous l'utiliser?
  • Qu'essayez-vous de faire avec le hash?
  • Avez-vous besoin d'une fonction de hachage cryptographiquement sécurisé?

Je vous suggère de jeter un coup d'œil à la famille Merkle-Damgard de fonctions de hachage telles que SHA-1, etc.

2
dirkgently

Je ne pense pas que nous puissions dire qu'une fonction de hachage est "bonne" sans connaître vos données à l'avance! et sans savoir ce que vous allez en faire.

Il existe de meilleures structures de données que les tables de hachage pour les tailles de données inconnues (je suppose que vous faites le hachage pour une table de hachage ici). Personnellement, je voudrais utiliser une table de hachage quand je sais que j'ai un nombre "fini" d'éléments qui ont besoin d'être stockés dans une quantité limitée de mémoire. J'essaierais de faire une analyse statistique rapide de mes données, de voir comment elles sont distribuées, etc. avant de commencer à penser à ma fonction de hachage.

1
Ouanixi

Des fonctions de hachage rapides et efficaces peuvent être composées en combinant plusieurs permutations rapides avec des qualités moindres, comme

  • multiplication avec un entier impair
  • rotations binaires
  • xorshift

Pour obtenir une fonction de hachage avec des qualités supérieures, comme démontré avec PCG pour la génération de nombres aléatoires.

C’est aussi la recette que rrxmrrxmsx_0 et le hash murmur utilisent, sciemment ou inconsciemment.

J'ai personnellement trouvé

uint64_t rol(const uint64_t& n,int i){
  return (n<<i)|(n>>(64-i);
}
uint64_t hash(const uint64_t& n){
  uint64_t c = random_uneven_64_bit_integer_constant"; 
  return c*rol(c*n,32);
}

être assez bon.

Ou vous pouvez utiliser des multiplications de champ de Galois telles que GHash , elles sont devenues raisonnablement rapides sur les processeurs modernes et possèdent des qualités supérieures en une étape.

0
Lykos

Pour des valeurs de hachage aléatoires, certains ingénieurs ont déclaré que le nombre premier du nombre d'or (2654435761) était un mauvais choix. D'après mes résultats de test, j'ai constaté que ce n'était pas vrai. au lieu de cela, 2654435761 distribue les valeurs de hachage plutôt bien.

#define MCR_HashTableSize 2^10

unsigned int
Hash_UInt_GRPrimeNumber(unsigned int key)
{
  key = key*2654435761 & (MCR_HashTableSize - 1)
  return key;
}

La taille de la table de hachage doit être une puissance de deux.

J'ai écrit un programme de test pour évaluer de nombreuses fonctions de hachage pour les entiers, les résultats montrent que GRPrimeNumber est un très bon choix.

J'ai essayé:

  1. total_data_entry_number/total_bucket_number = 2, 3, 4; où total_bucket_number = taille de la table de hachage;
  2. mappez le domaine de valeur de hachage dans le domaine d'index de compartiment; c'est-à-dire, convertissez la valeur de hachage en index de compartiment à l'aide de Logical And Operation avec (hash_table_size - 1), comme indiqué dans Hash_UInt_GRPrimeNumber ();
  3. calculer le nombre de collisions de chaque godet;
  4. enregistrer le seau qui n'a pas été mappé, c'est-à-dire un seau vide;
  5. trouver le nombre maximal de collisions de tous les compartiments; c'est-à-dire la plus longue longueur de chaîne;

Avec mes résultats de test, j'ai trouvé que le nombre premier en rapport Golden a toujours le moins de seaux vides ou de seaux vides et la longueur de chaîne de collision la plus courte.

Certaines fonctions de hachage pour les entiers sont supposées être bonnes, mais les résultats des tests montrent que lorsque total_data_entry/total_bucket_number = 3, la plus longue longueur de chaîne est supérieure à 10 (nombre maximal de collisions> 10) et que de nombreux compartiments ne sont pas mappés (compartiments vides). ), ce qui est très mauvais, comparé au résultat de zéro seau vide et plus longue chaîne 3 par Golden Ratio Prime Number Hashing.

BTW, avec mes résultats de test, j’ai trouvé qu’une version des fonctions de hachage de shifting-xor était plutôt bonne (elle est partagée par mikera).

unsigned int Hash_UInt_M3(unsigned int key)
{
  key ^= (key << 13);
  key ^= (key >> 17);    
  key ^= (key << 5); 
  return key;
}
0
Chen-ChungChia

J'ai utilisé splitmix64 _ (pointé dans le réponse ) de Thomas Mueller depuis que j'ai trouvé ce fil. Cependant, je suis récemment tombé sur rrxmrrxmsx_ de Pelle Evensen, qui a permis d'obtenir une distribution statistique considérablement meilleure que celle du finaliseur original MurmurHash3 et de ses successeurs (splitmix64 et autres mélanges). Voici l'extrait de code en C:

#include <stdint.h>

static inline uint64_t ror64(uint64_t v, int r) {
    return (v >> r) | (v << (64 - r));
}

uint64_t rrxmrrxmsx_0(uint64_t v) {
    v ^= ror64(v, 25) ^ ror64(v, 50);
    v *= 0xA24BAED4963EE407UL;
    v ^= ror64(v, 24) ^ ror64(v, 49);
    v *= 0x9FB21C651E98DF25UL;
    return v ^ v >> 28;
}

Pelle fournit également une analyse approfondie du mélangeur 64 bits utilisé dans la dernière étape de MurmurHash3 et les variantes les plus récentes.

0