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

Med*_*ine 48 c++ hash stl unordered-map c++11

我在用

unordered_map<string, int>
Run Code Online (Sandbox Code Playgroud)

unordered_map<int, int>
Run Code Online (Sandbox Code Playgroud)

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

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

Avi*_*sov 103

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

所有内置类型都存在标准专业化,还有一些其他标准库类型,如std::stringstd::thread.请参阅完整列表的链接.

对于要在a中使用的其他类型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)

  /// ...
Run Code Online (Sandbox Code Playgroud)

专业化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()); }
    };
Run Code Online (Sandbox Code Playgroud)

一些进一步的搜索引导我们:

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);
Run Code Online (Sandbox Code Playgroud)

_Hash_bytes是一个外部函数libstdc++.更多的搜索引导我到这个文件,其中指出:

// This file defines Hash_bytes, a primitive used for defining hash
// functions. Based on public domain MurmurHashUnaligned2, by Austin
// Appleby.  http://murmurhash.googlepages.com/
Run Code Online (Sandbox Code Playgroud)

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

  • @Medicine:Visual Studio的默认哈希算法(从VS2012开始)是[`Fowler-Noll-Vo`](http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80% 93Vo_hash_function)(FNV-1a). (11认同)
  • 谢谢@Avidanborisov我的字符串都是独一无二的,大小在14到21之间,包括英文字母_数字 (2认同)

Gab*_*les 10

GCC C++11 使用“MurmurHashUnaligned2”,作者 Austin Appleby

尽管散列算法依赖于编译器,但我将在 GCC C++11 中展示它。@Avidan Borisov 敏锐地发现用于字符串的 GCC 哈希算法是 Austin Appleby 的“MurmurHashUnaligned2”。我做了一些搜索,并在 Github 上找到了 GCC 的镜像副本。所以:

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

代码:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

Austin Appleby 散列函数的最新版本是“MurmerHash3”,已发布到公共领域!

奥斯汀在他的自述文件中说

SMHasher 套件还包括MurmurHash3,它是 MurmurHash 函数系列中的最新版本——新版本更快、更健壮,其变体可以在 x86 和 x64 平台上高效地生成 32 位和 128 位哈希值。

对于 MurmerHash3 的源代码,请参见此处:

  1. 杂音哈希3.h
  2. MurmurHash3.cpp

最棒的是!?它是公共领域的软件。这是正确的!文件的顶部状态:

// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
Run Code Online (Sandbox Code Playgroud)

所以,如果你想在你的开源软件、个人项目或专有软件中使用 MurmerHash3,包括用 C 实现你自己的哈希表,那就去吧!

如果你想要构建指令来构建和测试他的 MurmerHash3 代码,我在这里写了一些:https : //github.com/ElectricRCAircraftGuy/smhasher/blob/add_build_instructions/build/README.md。希望我打开的这个 PR被接受,然后它们最终会出现在他的主要回购中。但是,在那之前,请参阅我的 fork 中的构建说明。

对于其他散列函数,包括djb2和 2 个版本的 K&R 散列函数...

...(一个显然很糟糕,一个很好),请在此处查看我的另一个答案:string 的哈希函数

  • @lfmunoz,感谢您分享基准。但它们不是相同的算法。GCC 的 C++11 实现使用“MurmurHashUnaligned2”。`MurmerHash3` 是一个单独的算法,也是由 Austin Appleby 提出的。它们不是相同的算法。`MurmerHash3` 是他最新、最伟大的作品,并且不属于 GCC C++11。我认为“MurmerHash3”(速度)比 Murmer Hash 2 更好,否则将其作为后续产品发布几乎没有意义,除非这是一种权衡,它可能更慢,但有其他一些优势例如更好的桶分布和更少的碰撞。 (2认同)