我发现VS2005上的标准哈希函数在尝试实现高性能查找时非常慢。有哪些快速高效的哈希算法可以避免大部分冲突?
我发现VS2005上的标准哈希函数在尝试实现高性能查找时非常慢。有哪些快速高效的哈希算法可以避免大部分冲突?
我和微软研究员Paul Larson一起研究了一些哈希表实现。他在多个数据集上调查了许多字符串哈希函数,发现简单的101乘法加循环效果出奇地好。
unsigned int
hash(
const char* s,
unsigned int seed = 0)
{
unsigned int hash = seed;
while (*s)
{
hash = hash * 101 + *s++;
}
return hash;
}
s =“hello”
,
s的所有前缀的哈希值为:
[104, 10605, 1071213, 108192621, 2337520240]
如何快速找到子字符串hel
的哈希值? - Chitturi Sai Suman来自我之前的一些旧代码:
/* magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/ */
static const size_t InitialFNV = 2166136261U;
static const size_t FNVMultiple = 16777619;
/* Fowler / Noll / Vo (FNV) Hash */
size_t myhash(const string &s)
{
size_t hash = InitialFNV;
for(size_t i = 0; i < s.length(); i++)
{
hash = hash ^ (s[i]); /* xor the low 8 bits */
hash = hash * FNVMultiple; /* multiply by the magic number */
}
return hash;
}
它非常快。真的非常非常快。
^
是可传递的,这很糟糕。FNVMultiple
不是质数,这很糟糕。InitialFNV
也不是质数,这可能好也可能不好,我不确定。 - Mooing Duck这总是取决于你的数据集。
我本人使用字符串的CRC32得到了出乎意料的好结果。对于各种不同的输入集,它都非常有效。
网上有很多好的CRC32实现可供使用。
编辑:几乎忘记了:这个页面有一个漂亮的哈希函数比赛,附有性能数字和测试数据:
http://smallcode.weblogs.us/<--在页面下方。
对于字符串哈希,一个经典的建议是逐个遍历每个字母,将它们的ASCII/Unicode值加到累加器中,每次将累加器乘以质数。(允许哈希值溢出)
template <> struct myhash{};
template <> struct myhash<string>
{
size_t operator()(string &to_hash) const
{
const char * in = to_hash.c_str();
size_t out=0;
while(NULL != *in)
{
out*= 53; //just a prime number
out+= *in;
++in;
}
return out;
}
};
hash_map<string, int, myhash<string> > my_hash_map;
来自哈希函数全解析:
MurmurHash在游戏开发者圈子里相当受欢迎,被称为“通用哈希函数”。
这是一个不错的选择,但让我们看看是否可以找到更好的选择。如果你对数据了解得比“它将是未知数量的字节”更多,那么另一个不错的选择就是自己编写(例如,查看Won Chun的回复或Rune修改的xxHash/Murmur,专门针对4字节密钥等)。如果你了解你的数据,请尝试看看是否可以利用这些知识产生良好的效果!
如果没有更多的信息,我建议使用MurmurHash作为通用的非加密哈希函数。对于小字符串(程序中平均标识符的大小),非常简单而著名的djb2和FNV非常好。
在这里(数据大小小于10字节),我们可以看到其他算法的ILP智能并没有得到展示,而FNV或djb2的超级简单性在性能上胜出。
unsigned long
hash(unsigned char *str)
{
unsigned long hash = 5381;
int c;
while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
hash = FNV_offset_basis
for each byte_of_data to be hashed
hash = hash × FNV_prime
hash = hash XOR byte_of_data
return hash
hash = FNV_offset_basis
for each byte_of_data to be hashed
hash = hash XOR byte_of_data
hash = hash × FNV_prime
return hash
哈希函数可能会使您的代码容易受到拒绝服务攻击。如果攻击者能够强制您的服务器处理过多的冲突,您的服务器可能无法应对请求。
一些哈希函数(如MurmurHash)接受种子,您可以提供该种子以大大降低攻击者预测您的服务器软件生成的哈希值的能力。请记住这一点。