什么是C ++ std :: unordered_map中使用的默认哈希函数?

时间:2013-10-16 19:09:06

标签: c++ c++11 hash stl unordered-map

我正在使用

unordered_map<string, int>

unordered_map<int, int>

在每种情况下使用什么散列函数以及在每种情况下碰撞的可能性是多少? 我将分别在每种情况下插入唯一的字符串和唯一的int作为键。

我很想知道在字符串和int键及其碰撞统计数据的情况下哈希函数的算法。

2 个答案:

答案 0 :(得分:97)

使用了函数对象std::hash<>

所有内置类型和一些其他标准库类型都存在标准特化 例如std::stringstd::thread。请参阅完整列表的链接。

对于std::unordered_map中使用的其他类型,您必须专门化std::hash<>或创建自己的函数对象。

碰撞的可能性完全取决于实现,但考虑到整数限制在一个定义的范围之间,而字符串在理论上是无限长的,我会说有更好的机会与字符串冲突。

对于GCC中的实现,内置类型的专门化只返回位模式。以下是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)

  /// ...

std::string的专业化定义为:

#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()); }
    };

进一步搜索引导我们:

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是来自libstdc++的外部函数。更多的搜索引导我this file,其中指出:

// This file defines Hash_bytes, a primitive used for defining hash
// functions. Based on public domain MurmurHashUnaligned2, by Austin
// Appleby.  http://murmurhash.googlepages.com/

因此,GCC用于字符串的默认哈希算法是MurmurHashUnaligned2。

答案 1 :(得分:4)

虽然散列算法依赖于编译器,但我会将它呈现给GCC C ++ 11。 @Avidan Borisov astutely discovered用于字符串的GCC哈希算法是奥斯汀阿普尔比的“MurmurHashUnaligned2”。我做了一些搜索,在Github上找到了GCC的镜像副本。因此:

用于unordered_map(哈希表模板)和unordered_set(哈希集模板)的GCC C ++ 11哈希函数似乎如下所示。 < / p>

代码:

df[10,] = c(2015, 5)   #Add only one data for the year 2015
library(reshape2)
dcast(df, ave(df$Male, df$year, FUN = seq_along) ~ year, value.var = "Male")[,-1]
#  2011 2012 2013 2015
#1    8    3    4    5
#2    1   12    3   NA
#3    4    9    3   NA

对于其他散列函数,包括// 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; } ,以及K&amp; R散列函数的两个版本(一个显然非常糟糕,一个非常好),请参阅我的其他答案:{ {3}}