与std::hash发生意外冲突
Unexpected collision with std::hash
我知道将无限数量的字符串散列成32b int必须产生冲突,但我期望从散列函数中得到一些不错的分布。
这两个字符串有相同的哈希值是不是很奇怪?
size_t hash0 = std::hash<std::string>()("generated_id_0");
size_t hash1 = std::hash<std::string>()("generated_id_1");
//hash0 == hash1
我知道我可以使用boost::hash<std::string>
或其他,但我想知道std::hash
有什么问题。我用错了吗?难道我不应该"播种"它吗?
您使用std::hash
没有任何问题。问题是Visual Studio 2010附带的标准库实现提供的专门化std::hash<std::string>
只接受字符串字符的子集来确定哈希值(可能是出于性能原因)。巧合的是,有14个字符的字符串的最后一个字符不属于这个集合,这就是为什么两个字符串产生相同的哈希值。
据我所知,这种行为是符合标准的,要求只是多次调用具有相同参数的哈希函数必须始终返回相同的值。然而,哈希冲突的概率应该是最小的。VS2010实现完成了强制的部分,但是没有考虑可选的部分。
有关详细信息,请参阅c++标准的头文件xfunctional
(在我的副本中从第869行开始)和§17.6.3.4中的实现(最新的公开草案)。
如果你绝对需要一个更好的字符串哈希函数,你应该自己实现它。其实没那么难。
标准中没有指定确切的哈希算法,因此结果会有所不同。VC10使用的算法似乎并没有把所有的如果字符串长度超过10个字符,则考虑字符;它以1 + s.size() / 10
的增量前进。这是合法的,虽然从qi的角度来看,相当令人失望;这样的哈希码对于某些典型的数据集(例如url)。我强烈建议您将其替换为FNV散列或基于梅森素数的一个:
FNV散列:
struct hash
{
size_t operator()( std::string const& s ) const
{
size_t result = 2166136261U ;
std::string::const_iterator end = s.end() ;
for ( std::string::const_iterator iter = s.begin() ;
iter != end ;
++ iter ) {
result = (16777619 * result)
^ static_cast< unsigned char >( *iter ) ;
}
return result ;
}
};
梅森素数哈希:
struct hash
{
size_t operator()( std::string const& s ) const
{
size_t result = 2166136261U ;
std::string::const_iterator end = s.end() ;
for ( std::string::const_iterator iter = s.begin() ;
iter != end ;
++ iter ) {
result = 127 * result
+ static_cast< unsigned char >( *iter ) ;
}
return result ;
}
};
(FNV散列应该更好,但梅森素数散列会更好在很多机器上更快,因为乘以127经常明显快于乘以16777619)
您可能会得到不同的散列值。我得到不同的哈希值(GCC 4.5):
<标题> hashtest.cpp h1> 输出# g++ hashtest.cpp -o hashtest -std=gnu++0x
# ./hashtest
16797002355621538189 != 16797001256109909978
标题>
你没有种子哈希函数,你最多只能盐"他们"。
函数的使用方式正确,这种碰撞可能只是偶然的。
你无法判断哈希函数是否不是均匀分布的,除非你用随机键进行大量的测试。
TR1哈希函数和最新的标准为字符串之类的东西定义了适当的重载。当我使用std::tr1::hash (g++ 4.1.2)运行这段代码时,我得到了这两个字符串的不同哈希值。
- 写入位置0x0000000C时发生访问冲突
- 在C++中对T*类型执行std::move的意外行为
- GL_SHADERSTORAGE_BUFFER位置是否与其他着色器位置冲突
- 使用cmake从源代码构建MySQL连接器/C++失败(与以前的声明冲突)
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 引发异常:读取访问冲突**dynamicArray**为0x1118235.发生
- 处理除以零会导致<csignal>意外行为
- C++LinkedList问题.数据类型之间存在冲突?没有匹配的构造函数
- vscode下的Arduino代码出现意外编译错误
- 使用++运算符会导致意外的结果
- 套接字读取后,我在缓冲区中看到意外输入
- 链表中写入访问冲突的未知原因
- 更改.cpp程序的输入文件中数据的位置会意外更改输出
- C++中的openCV Mat访问冲突
- 使用vscode调试时,GDB意外退出
- 如何使 std::sort 在 std::swap 和我的命名空间的模板化交换之间没有名称冲突?
- 此测试()中发生了什么意外过程?为什么总是覆盖 ch[0 1 2..]?
- 尝试将字符串/字符转换为整数会产生意外结果
- 意外的 Acess 冲突 (itoa)
- 与std::hash发生意外冲突