C++ std::unordered_map 中使用的默认哈希函数是什么
What is the default hash function used in C++ std::unordered_map?
我正在使用
unordered_map<string, int>
和
unordered_map<int, int>
每种情况下使用什么哈希函数,每种情况下发生冲突的几率是多少?我将分别在每种情况下插入唯一字符串和唯一 int 作为键。
我有兴趣了解字符串和整数键情况下的哈希函数算法及其冲突统计信息。
使用函数对象std::hash<>
。
所有内置类型以及一些其他标准库类型都存在标准专用化如std::string
和std::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 defines Hash_bytes, a primitive used for defining hash
// functions. Based on public domain MurmurHashUnaligned2, by Austin
// Appleby. http://murmurhash.googlepages.com/
因此,GCC 用于字符串的默认哈希算法是 MurmurHashUnaligned2。
GCC C++11 使用 "MurmurHashUnaligned2",作者:Austin Appleby
虽然哈希算法依赖于编译器,但我将在GCC C++11中介绍它。 @Avidan Borisov敏锐地发现,用于字符串的GCC哈希算法是Austin Appleby的"MurmurHashUnaligned2"。我做了一些搜索,在Github上找到了GCC的镜像副本。因此:
用于unordered_map
(哈希表模板)和unordered_set
(哈希集模板)的 GCC C++11 哈希函数如下所示。
- 感谢Avidan Borisov的背景研究,关于GCC C++11哈希函数使用什么的问题,说明GCC使用Austin Appleby的"MurmurHashUnaligned2"的实现(见 http://murmurhash.googlepages.com/和 https://github.com/aappleby/smhasher)。
- 在文件"gcc/libstdc++-v3/libsupc++/hash_bytes.cc"中,在这里(https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/hash_bytes.cc),我找到了实现。例如,这是"32 位size_t"返回值(2017 年 8 月 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;
}
Austin Appleby的哈希函数的最新版本是"MurmurHash3",它已发布到公共领域!
奥斯汀在他的自述文件中说:
SMHasher套件还包括MurmurHash3,这是MurmurHash系列函数中的最新版本 - 新版本更快,更健壮,其变体可以在x86和x64平台上有效地产生32位和128位哈希值。
有关MurmurHash3的源代码,请参见此处:
- 杂音哈希3.h
- 杂音哈希3.cpp
最棒的是!?它是公共领域的软件。没错!文件的顶部指出:
// MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code.
因此,如果您想在开源软件,个人项目或专有软件中使用MurmurHash3,包括用C实现自己的哈希表,那就去吧!
如果你想构建和测试他的MurmurHash3代码,我在这里写了一些:https://github.com/ElectricRCAircraftGuy/smhasher/blob/add_build_instructions/build/README.md。希望我打开的这个 PR 被接受,然后它们最终会进入他的主存储库。但是,在此之前,请参阅我的分叉中的构建说明。
对于其他哈希函数,包括djb2
,以及K&R哈希函数的2个版本...
。(一个显然很糟糕,一个相当不错),请参阅我的另一个答案:字符串的哈希函数。
另请参阅:
- https://en.wikipedia.org/wiki/MurmurHash
- 进一步研究要做:看看这些哈希函数速度基准:https://github.com/fredrikwidlund/hash-function-benchmark(感谢@lfmunoz指出这一点)
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 使用Qt C++计算类似Git的SHA1哈希
- 如何将这个C++哈希表转换为动态扩展和收缩,而不是使用硬设置的最大值
- 用C++将哈希表写入文件并从文件中恢复
- c++找不到具有相同哈希的无序集合元素
- 哈希文件递归并保存到矢量Cryptopp中
- 对 pair<pair<int,int>pair<int,int unordered_map进行哈希处理>>
- 直接在 unordered_map 的方法中使用哈希,而不是生成哈希的用户定义对象
- 如何为字符串生成唯一但一致的 N 位哈希(小于 64 位)?
- 使用对象的基类部分模板专用化对对象进行哈希处理::哈希
- boost::包含提升单元的元组的哈希值
- 使用 Key 对 C++ 中的哈希映射进行排序. 无法排序
- C++中的并发哈希表
- 哈希映射使用 nullptr c++ 初始化节点的动态数组
- 在具有开放寻址的哈希表中插入节点 [优化逻辑]
- 与C++哈希表的基础知识混淆
- CRC32 哈希默认值/无效值?
- 默认情况下,使 std 的数据结构使用我现有的非静态哈希函数"hashCode()"
- C++ std::unordered_map 中使用的默认哈希函数是什么