我需要在C ++中使用面向性能的哈希函数实现来实现我将要编码的哈希表。我已经环顾四周,只发现了一个问题,询问什么是“一般”的好散列函数。我已经考虑过CRC32(但哪里可以找到好的实现?)和一些加密算法。不过,我的表格有非常具体的要求。
这是表格的样子:
100,000 items max
200,000 capacity (so the load is 0.5)
hashing a 6-character string which is a part of English sentence
examples: "become" "and he" ", not "
我的哈希表的第一优先级是快速搜索(检索)。快速插入并不重要,但它会伴随快速搜索。删除并不重要,重新哈希不是我要研究的东西。为了处理冲突,我可能正在使用单独的链接,如here所述。我已经看过this article了,但是想要对那些曾经处理过这样的任务的人提出意见。
答案 0 :(得分:24)
现在假设你想要一个哈希,并想要一些能够在你的情况下工作的快速,因为你的字符串只有6个字符长,你可以使用这个魔法:
size_t precision = 2; //change the precision with this
size_t hash(const char* str)
{
return (*(size_t*)str)>> precision;
}
CRC用于慢速播放;)
<强>说明:强> 这是通过将字符串指针的内容转换为“看起来像”size_t(int32或int64,基于硬件的最佳匹配)来实现的。因此,字符串的内容被解释为原始数字,不再担心字符,然后您将所需的精度进行位移(您将此数字调整为最佳性能,我发现2对于散列字符串很有效)成千上万)。
另外,真正整洁的部分是现代硬件上任何体面的编译器都会在1个汇编指令中散列这样的字符串,很难被击败;)
答案 1 :(得分:13)
这个简单的多项式非常有效。我从微软研究院的Paul Larson那里得到了它,他研究了各种哈希函数和哈希乘法器。
unsigned hash(const char* s, unsigned salt)
{
unsigned h = salt;
while (*s)
h = h * 101 + (unsigned) *s++;
return h;
}
在创建哈希表以防御hash table attacks之前,应将 salt
初始化为某些随机选择的值。如果这对您来说不是问题,请使用0。
表格的大小也很重要,以尽量减少碰撞。听起来像你的很好。
答案 2 :(得分:6)
Boost.Functional/Hash可能对您有用。我没试过,所以我不能保证它的表现。
Boost也有CRC library。
我首先看Boost.Unordered(即boost :: unordered_map&lt;&gt;)。它使用哈希映射而不是二进制树作为容器。
我相信一些STL实现有一个hash_map&lt;&gt; stdext命名空间中的容器。
答案 3 :(得分:4)
表的大小将决定您应该使用的大小哈希值。您希望尽可能减少碰撞。我不确定你用最大项目和容量指定了什么(它们对我来说似乎是一样的)在任何情况下,这些数字中的任何一个都表明32位散列就足够了。您可能会使用CRC16(约65,000种可能性),但您可能会遇到很多冲突。另一方面,碰撞可能比CRC32散列更快地处理。
我想说,请使用CRC32。您会发现文档和示例代码不缺。由于您已经计算出最大值并且速度是优先级,因此请使用指针数组。使用哈希生成索引。在碰撞时,增加索引直到你碰到一个空桶..快速而简单。
答案 4 :(得分:4)
由于您存储英文单词,因此您的大多数字符都是字母,并且数据的最重要的两位不会有太大的变化。除此之外,我会保持它非常简单,只需使用XOR。毕竟你不是在寻找加密强度,而只是为了合理均匀分布。这些方面的东西:
size_t hash(const std::string &data) {
size_t h(0);
for (int i=0; i<data.length(); i++)
h = (h << 6) ^ (h >> 26) ^ data[i];
}
return h;
}
除此之外,您是否将std :: tr1 :: hash视为哈希函数和/或std :: tr1 :: unordered_map作为哈希表的实现?使用这些可能会节省很多与实现自己的类相反的工作。
答案 5 :(得分:2)
如果您需要搜索短字符串并且插入不是问题,也许您可以使用B树或2-3树,在您的情况下通过散列不会获得太多收益。
你这样做的方法是在每个节点上放一个字母,这样你首先要检查节点“a”,然后检查“a”的子节点是否为“p”,它是“p”的子节点,然后是“l”然后是“e”。在你有“苹果”和“申请”的情况下,你需要寻找最后一个节点,(因为唯一的区别在于最后一个“e”和“y”)
但是在大多数情况下,你只需几步就可以得到这个词(“xylophone”=&gt;“x” - &gt;“ylophone”),所以你可以这样优化。这可能比散列更快
答案 6 :(得分:2)
哈希表的首要任务是快速搜索(检索)。
那么你使用正确的数据结构,因为在哈希表中搜索是O(1)! :)
CRC32应该没问题。实现并不复杂,它主要基于XOR。只要确保它使用一个好的多项式。
答案 7 :(得分:2)
简单的事情:
// Initialize hash lookup so that it maps the characters
// in your string to integers between 0 and 31
int hashLookup[256];
// Hash function for six character strings.
int hash(const char *str)
{
int ret = 0, mult = 1;
for (const char *p = str; *p; *p++, mult *= 32) {
assert(*p >= 0 && *p < 256);
ret += mult * hashLookup[*p];
}
return ret;
}
这假定为32位整数。它每个字符使用5位,因此哈希值只有30位。你可以通过为前一个或两个字符生成六位来解决这个问题。如果字符集足够小,则可能不需要超过30位。
答案 8 :(得分:0)
从C ++ 11开始,C ++提供了std::hash< string >( string )
。这可能是一种有效的散列函数,可为大多数字符串提供good distribution of hash-codes。
此外,如果您正在考虑实现哈希表,那么您现在应该考虑使用C ++ std::unordered_map
。