J'ai beaucoup de choses nommées sans rapport avec lesquelles j'aimerais effectuer des recherches rapides. Un "aardvark" est toujours un "aardvark" partout, donc hacher la chaîne et réutiliser l'entier fonctionnerait bien pour accélérer les comparaisons. L'ensemble des noms est inconnu (et change avec le temps). Qu'est-ce qu'un algorithme de hachage de chaîne rapide qui générera de petites valeurs (32 ou 16) bits et aura un faible taux de collision?
J'aimerais voir une implémentation optimisée spécifique à C/C++.
L'une des variantes FNV devrait répondre à vos besoins. Ils sont rapides et produisent des sorties distribuées assez uniformément.
Murmur Hash est plutôt sympa.
Il y a aussi un Nice article à eternallyconfuzzled.com .
Le hachage One-at-a-Time de Jenkins pour les chaînes devrait ressembler à ceci:
#include <stdint.h>
uint32_t hash_string(const char * s)
{
uint32_t hash = 0;
for(; *s; ++s)
{
hash += *s;
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
Pour un ensemble de chaînes fixe, utilisez gperf.
Si votre chaîne change, vous devez choisir une fonction de hachage. Ce sujet a déjà été discuté:
Une autre solution qui pourrait être encore meilleure en fonction de votre cas d'utilisation est chaînes internes. Voici comment fonctionnent les symboles, par ex. dans LISP.
Une chaîne internée est un objet chaîne dont la valeur est l'adresse des octets de chaîne réels. Vous créez donc un objet chaîne internée en archivant une table globale: si la chaîne s'y trouve, vous initialisez la chaîne internée à l'adresse de cette chaîne. Sinon, vous l'insérez, puis initialisez votre chaîne interne.
Cela signifie que deux chaînes internes construites à partir de la même chaîne auront la même valeur, qui est une adresse. Donc, si N est le nombre de chaînes internes à votre système, les caractéristiques sont les suivantes:
À votre santé,
Carl
Il n'est jamais tard pour un bon sujet et je suis sûr que les gens seraient intéressés par mes conclusions.
J'avais besoin d'une fonction de hachage et après avoir lu ce post et fait un peu de recherche sur les liens donnés ici, j'ai trouvé cette variation de l'algorithme de Daniel J Bernstein, que j'ai utilisé pour faire un test intéressant:
unsigned long djb_hashl(const char *clave)
{
unsigned long c,i,h;
for(i=h=0;clave[i];i++)
{
c = toupper(clave[i]);
h = ((h << 5) + h) ^ c;
}
return h;
}
</code>
Cette variante hache les chaînes en ignorant le cas, ce qui correspond à mon besoin de hacher les informations de connexion des utilisateurs. "clave" est "clé" en espagnol. Je suis désolé pour l'espagnol mais c'est ma langue maternelle et le programme est écrit dessus.
Eh bien, j'ai écrit un programme qui générera des noms d'utilisateur de 'test_aaaa' à 'test_zzzz', et -pour allonger les chaînes- je leur ai ajouté un domaine aléatoire dans cette liste: 'cloud-nueve.com', 'yahoo.com "," gmail.com "et" hotmail.com ". Par conséquent, chacun d'eux ressemblerait à:
[email protected], [email protected], [email protected], [email protected] et ainsi de suite.
Voici la sortie du test -'Colision entre XXX y XXX 'signifie' Collision de XXX et XXX '. "palabras" signifie "mots" et "Total" est le même dans les deux langues.
Buscando Colisiones ... Colision entre '[email protected]' et '[email protected]' (1DB903B7) Colision entre ' [email protected] 'y' [email protected] '(2F5BC088) Colision entre' [email protected] 'y' [email protected] '(51FD09CC) Colision entre '[email protected]' et '[email protected]' (52F5480E) Colision entre '[email protected]' y '[email protected]' (74FF72E2) Colision entre '[email protected]' et '[email protected]' (7FD70008) Colision entre '[email protected]' y '[email protected]' (9BD351C4) Colision entre '[email protected]' y '[email protected]' (A86953E1) Colision entre '[email protected]' y '[email protected]' ( BA6B0718) Colision entre '[email protected]' et '[email protected]' (D0523F88) Colision entre '[email protected]' y '[email protected]' ( DEE0 8108) Total de Colisiones: 11 Total de Palabras: 456976
Ce n'est pas mal, 11 collisions sur 456 976 (bien sûr en utilisant le 32 bits complet comme longueur de table).
L'exécution du programme en utilisant 5 caractères, c'est-à-dire de 'test_aaaaa' à 'test_zzzzz', manque en fait de mémoire pour construire la table. Ci-dessous la sortie. "No hay memoria para insertar XXXX (insertadas XXX)" signifie "Il n'y a plus de mémoire pour insérer XXX (XXX inséré)". Fondamentalement, malloc () a échoué à ce stade.
Aucune mémoire de foin para insertar 'test_epjcv' (insertadas 2097701). Buscando Colisiones ... .. .451 'colision' strings ... Total de Colisiones: 451 Total de Palabras: 2097701
Ce qui signifie seulement 451 collisions sur 2 097 701 chaînes. Notez que dans aucune des occasions, il n'y a eu plus de 2 collisions par code. Ce que je confirme, c'est un bon hachage pour moi, car ce dont j'ai besoin est de convertir l'ID de connexion en un identifiant unique de 40 bits pour l'indexation. J'utilise donc cela pour convertir les informations d'identification de connexion en un hachage 32 bits et utiliser les 8 bits supplémentaires pour gérer jusqu'à 255 collisions par code, ce qui ressemblerait aux résultats du test serait presque impossible à générer.
J'espère que cela sera utile à quelqu'un.
MODIFIER:
Comme la boîte de test est AIX, je l'exécute en utilisant LDR_CNTRL = MAXDATA = 0x20000000 pour lui donner plus de mémoire et il s'exécute plus longtemps, les résultats sont ici:
Buscando Colisiones ... Total de Colisiones: 2908 Total de Palabras: 5366384
Soit 2908 après 5 366 384 essais !!
TRÈS IMPORTANT: Compilation du programme avec -maix64 (donc non signé long est 64 bits), le nombre de collisions est 0 pour tous les cas !!!
Pourquoi ne pas simplement utiliser bibliothèques Boost? Leur fonction de hachage est simple à utiliser et la plupart des éléments de Boost feront bientôt partie de la norme C++. Certains le sont déjà.
Booster le hachage est aussi simple que
#include <boost/functional/hash.hpp>
int main()
{
boost::hash<std::string> string_hash;
std::size_t h = string_hash("Hash me");
}
Vous pouvez trouver boost sur boost.org
Bob Jenkins a de nombreuses fonctions de hachage disponibles , qui sont toutes rapides et ont de faibles taux de collision.
Jetez un œil à GNU gperf .
Vous pouvez voir ce que .NET utilise sur la méthode String.GetHashCode () à l'aide de Reflector.
Je risquerais de supposer que Microsoft a passé beaucoup de temps à l'optimiser. Ils ont également imprimé dans toute la documentation MSDN qu'elle est susceptible de changer tout le temps. C'est donc clairement sur leur "radar de réglage des performances" ;-)
Ce serait assez trivial de porter en C++ aussi, j'aurais pensé.
Il y a une bonne discussion dans ce question précédente
Et un bon aperçu de la façon de choisir les fonctions de hachage, ainsi que des statistiques sur la distribution de plusieurs fonctions courantes ici
Décrit ici est un moyen simple de l'implémenter vous-même: http://www.devcodenote.com/2015/04/collision-free-string-hashing.html
Un extrait du post:
si disons que nous avons un jeu de lettres majuscules en anglais, alors la longueur du jeu de caractères est 26 où A pourrait être représenté par le chiffre 0, B par le chiffre 1, C par le chiffre 2 et ainsi de suite jusqu'à Z par le chiffre 25. Maintenant, chaque fois que nous voulons mapper une chaîne de ce jeu de caractères à un nombre unique, nous effectuons la même conversion que nous l'avons fait dans le cas du format binaire