Quelle est la fonction de hachage par défaut utilisée dans C++ std::unordered map?
j'utilise
unordered_map<string, int>
et
unordered_map<int, int>
quelle fonction de hachage est utilisée dans chaque cas et quel est le risque de collision dans chaque cas? Je vais insérer une chaîne unique et unique int comme clés dans chaque cas respectivement.
je suis intéressé de connaître l'algorithme de la fonction de hachage dans le cas des touches string et int et leurs statistiques de collision.
2 réponses
l'objet de fonction std::hash<>
est utilisé.
il existe des spécialisations Standard pour tous les types de bibliothèques intégrées et certains autres types de bibliothèques standard.
std::string
et std::thread
. Voir le lien pour la liste complète.
pour les autres types à utiliser dans un std::unordered_map
, vous devrez vous spécialiser std::hash<>
ou créer votre propre objet de fonction.
le risque de collision dépend entièrement de l'implémentation, mais en considérant le fait que les entiers sont limités entre une portée définie, tandis que les cordes sont théoriquement infiniment longues, je dirais qu'il y a une bien meilleure chance de collision avec les cordes.
en ce qui concerne L'implémentation dans GCC, la spécialisation pour les types de construction renvoie simplement le patron de bits. Voici comment ils sont définis dans bits/functional_hash.h
:
/// Partial specializations for pointer types.
template<typename _Tp>
struct hash<_Tp*> : public __hash_base<size_t, _Tp*>
{
size_t
operator()(_Tp* __p) const noexcept
{ return reinterpret_cast<size_t>(__p); }
};
// Explicit specializations for integer types.
#define _Cxx_hashtable_define_trivial_hash(_Tp) \
template<> \
struct hash<_Tp> : public __hash_base<size_t, _Tp> \
{ \
size_t \
operator()(_Tp __val) const noexcept \
{ return static_cast<size_t>(__val); } \
};
/// Explicit specialization for bool.
_Cxx_hashtable_define_trivial_hash(bool)
/// Explicit specialization for char.
_Cxx_hashtable_define_trivial_hash(char)
/// ...
La spécialisation pour std::string
est défini comme suit:
#ifndef _GLIBCXX_COMPATIBILITY_CXX0X
/// std::hash specialization for string.
template<>
struct hash<string>
: public __hash_base<size_t, string>
{
size_t
operator()(const string& __s) const noexcept
{ return std::_Hash_impl::hash(__s.data(), __s.length()); }
};
D'autres recherches nous mènent à:
struct _Hash_impl
{
static size_t
hash(const void* __ptr, size_t __clength,
size_t __seed = static_cast<size_t>(0xc70f6907UL))
{ return _Hash_bytes(__ptr, __clength, __seed); }
...
};
...
// Hash function implementation for the nontrivial specialization.
// All of them are based on a primitive that hashes a pointer to a
// byte array. The actual hash algorithm is not guaranteed to stay
// the same from release to release -- it may be updated or tuned to
// improve hash quality or speed.
size_t
_Hash_bytes(const void* __ptr, size_t __len, size_t __seed);
_Hash_bytes
est une fonction externe de libstdc++
. Un peu plus de recherche m'a conduit à ce fichier, qui stipule:
// This file defines Hash_bytes, a primitive used for defining hash
// functions. Based on public domain MurmurHashUnaligned2, by Austin
// Appleby. http://murmurhash.googlepages.com/
donc L'algorithme de hachage utilisé par défaut par GCC pour les chaînes est MurmurHashUnaligned2.
bien que les algorithmes de hachage soient dépendants du compilateur, je vais le présenter pour GCC c++11. @Avidan Borisov découvert astucieusement que L'algorithme de hachage GCC utilisé pour les chaînes est "MurmurHashUnaligned2", par Austin Appleby. J'ai cherché et j'ai trouvé une copie de GCC sur Github. Donc:
les fonctions de hachage GCC c++11 utilisées pour unordered_map
(un modèle de table de hachage) et unordered_set
(un modèle de jeu de hachures) semble être comme suit.
- merci à Avidan Borisov pour ses recherches de fond qui sur la question de quelles sont les fonctions de hachage GCC c++11 utilisées, indiquant que GCC utilise une implémentation de "MurmurHashUnaligned2", par Austin Appleby (http://murmurhash.googlepages.com/).
- dans le fichier "gcc / libstdc++ - v3/libsupc++/hash_bytes.cc", ici (https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/hash_bytes.cc)j'ai trouvé les implémentations. Voici celui de la valeur de retour" 32-bit size_t", par exemple (tiré le 11 août 2017)
Code:
// Implementation of Murmur hash for 32-bit size_t.
size_t _Hash_bytes(const void* ptr, size_t len, size_t seed)
{
const size_t m = 0x5bd1e995;
size_t hash = seed ^ len;
const char* buf = static_cast<const char*>(ptr);
// Mix 4 bytes at a time into the hash.
while (len >= 4)
{
size_t k = unaligned_load(buf);
k *= m;
k ^= k >> 24;
k *= m;
hash *= m;
hash ^= k;
buf += 4;
len -= 4;
}
// Handle the last few bytes of the input array.
switch (len)
{
case 3:
hash ^= static_cast<unsigned char>(buf[2]) << 16;
[[gnu::fallthrough]];
case 2:
hash ^= static_cast<unsigned char>(buf[1]) << 8;
[[gnu::fallthrough]];
case 1:
hash ^= static_cast<unsigned char>(buf[0]);
hash *= m;
};
// Do a few final mixes of the hash.
hash ^= hash >> 13;
hash *= m;
hash ^= hash >> 15;
return hash;
}
pour les fonctions de hachage supplémentaires, y compris djb2
, et les 2 versions des fonctions de hachage K&R (apparemment terrible, une assez bonne), voir mon autre réponse ici: https://stackoverflow.com/a/45641002/4561887.