我有许多不相关的命名内容,我想快速搜索。 “aardvark”在任何地方始终都是“aardvark”,因此对字符串进行散列并重用整数可以很好地加速比较。整个名称集是未知的(并随着时间的推移而变化)。什么是快速字符串哈希算法,它将生成小(32或16)位值并具有低冲突率?
我希望看到一个特定于C / C ++的优化实现。
答案 0 :(得分:32)
Murmur Hash非常好。
答案 1 :(得分:29)
其中一个FNV variants应符合您的要求。它们很快,并且产生相当均匀的分布式输出。
答案 2 :(得分:17)
对于固定的字符串集,请使用gperf。
如果您的字符串集发生了变化,您必须选择一个哈希函数。之前已讨论过该主题:
What's the best hashing algorithm to use on a stl string when using hash_map?
答案 3 :(得分:17)
nice article还有一个eternallyconfuzzled.com。
詹金斯对字符串的一次性哈希应该是这样的:
#include <stdint.h>
uint32_t hash_string(const char * s)
{
uint32_t hash = 0;
for(; *s; ++s)
{
hash += *s;
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
答案 4 :(得分:8)
根据您的使用案例,可能更好的另一种解决方案是 interned strings 。这就是符号如何工作,例如在Lisp。
interned string是一个字符串对象,其值是实际字符串字节的地址。因此,您通过检入全局表来创建一个实习字符串对象:如果字符串在那里,则将实习字符串初始化为该字符串的地址。如果没有,则插入它,然后初始化您的实习字符串。
这意味着从同一个字符串构建的两个实习字符串将具有相同的值,即一个地址。因此,如果N是系统中实习字符串的数量,则特征为:
干杯,
卡尔
答案 5 :(得分:4)
为什么不使用Boost libraries?它们的散列函数使用起来很简单,Boost中的大部分内容很快就会成为C ++标准的一部分。其中一些已经是。
Boost哈希就像
一样简单#include <boost/functional/hash.hpp>
int main()
{
boost::hash<std::string> string_hash;
std::size_t h = string_hash("Hash me");
}
您可以在boost.org
找到提升答案 6 :(得分:4)
一个好主题永远不会迟到,我相信人们会对我的发现感兴趣。
我需要一个哈希函数,在阅读这篇文章并对这里给出的链接做了一些研究后,我想出了Daniel J Bernstein算法的这种变体,我曾经做过一个有趣的测试:
unsigned long djb_hashl(const char *clave)
{
unsigned long c,i,h;
for(i=h=0;clave[i];i++)
{
c = toupper(clave[i]);
h = ((h << 5) + h) ^ c;
}
return h;
}
unsigned long djb_hashl(const char *clave)
{
unsigned long c,i,h;
for(i=h=0;clave[i];i++)
{
c = toupper(clave[i]);
h = ((h << 5) + h) ^ c;
}
return h;
}
这种变化会使字符串忽略大小写,这符合我对哈希用户登录凭据的需要。 'clave'是西班牙语中的“关键”。我很抱歉西班牙语,但我的母语和程序都写在上面。
好吧,我编写了一个程序,它将从'test_aaaa'生成用户名到'test_zzzz',并且 - 为了使字符串更长 - 我在这个列表中添加了一个随机域:'cloud-nueve.com',' yahoo.com','gmail.com'和'hotmail.com'。因此,每个人看起来都像:
test_aaaa@cloud-nueve.com, test_aaab@yahoo.com, test_aaac@gmail.com, test_aaad@hotmail.com and so on.
以下是测试的输出-'Colision entre XXX y XXX'表示'XXX和XXX的碰撞'。 'palabras'表示'words','Total'在两种语言中都相同 - 。
Buscando Colisiones... Colision entre 'test_phiz@hotmail.com' y 'test_juxg@cloud-nueve.com' (1DB903B7) Colision entre 'test_rfhh@hotmail.com' y 'test_fpgo@yahoo.com' (2F5BC088) Colision entre 'test_wxuj@hotmail.com' y 'test_pugy@cloud-nueve.com' (51FD09CC) Colision entre 'test_sctb@gmail.com' y 'test_iohw@cloud-nueve.com' (52F5480E) Colision entre 'test_wpgu@cloud-nueve.com' y 'test_seik@yahoo.com' (74FF72E2) Colision entre 'test_rfll@hotmail.com' y 'test_btgo@yahoo.com' (7FD70008) Colision entre 'test_wcho@cloud-nueve.com' y 'test_scfz@gmail.com' (9BD351C4) Colision entre 'test_swky@cloud-nueve.com' y 'test_fqpn@gmail.com' (A86953E1) Colision entre 'test_rftd@hotmail.com' y 'test_jlgo@yahoo.com' (BA6B0718) Colision entre 'test_rfpp@hotmail.com' y 'test_nxgo@yahoo.com' (D0523F88) Colision entre 'test_zlgo@yahoo.com' y 'test_rfdd@hotmail.com' (DEE08108) Total de Colisiones: 11 Total de Palabras : 456976
这还不错,456,976中的11次冲突(当然使用完整的32位作为表格长度)。
使用5个字符运行程序,即从'test_aaaaa'到'test_zzzzz',实际上耗尽了构建表的内存。以下是输出。 '没有干草memoria para insertar XXXX(insertadas XXX)'表示'没有留下内存插入XXX(插入XXX)'。基本上malloc()在那时失败了。
No hay memoria para insertar 'test_epjcv' (insertadas 2097701). Buscando Colisiones... ...451 'colision' strings... Total de Colisiones: 451 Total de Palabras : 2097701
这意味着在2,097,701个字符串上只发生451次碰撞。请注意,在任何情况下,每个代码都有超过2次冲突。我确认它对我来说是一个很好的哈希,因为我需要的是将登录ID转换为40位唯一ID进行索引。因此,我使用它将登录凭据转换为32位散列,并使用额外的8位来处理每个代码最多255次冲突,这样几乎不可能生成测试结果。
希望这对某人有用。
修改强>
就像测试盒是AIX一样,我使用LDR_CNTRL = MAXDATA = 0x20000000来运行它以给它更多内存并且运行时间更长,结果在这里:
Buscando Colisiones ... Total de Colisiones:2908 Total de Palabras:5366384
在5,366,384次尝试之后是2908 !!
非常重要:使用-maix64编译程序(因此无符号长度为64位),所有情况下的冲突数为0!
答案 7 :(得分:3)
看看GNU gperf。
答案 8 :(得分:3)
Hsieh哈希函数非常好,并且有一些基准/比较,作为C中的一般哈希函数。根据你想要的东西(它不是很明显)你可能想要考虑类似{{{ 3}}而不是。
答案 9 :(得分:3)
Bob Jenkins has many hash functions available,所有这些都很快且碰撞率很低。
答案 10 :(得分:2)
关于如何选择哈希函数的概述,以及关于几个常见函数的分布的统计信息here
答案 11 :(得分:2)
您可以使用Reflector查看.NET在String.GetHashCode()方法中使用的内容。
我猜想微软花了相当多的时间来优化它。他们也在所有MSDN文档中打印过,它一直在变化。很明显,这是他们的“性能调整雷达”; - )
我会想到移植到C ++也是非常简单的。
答案 12 :(得分:0)
这里描述的是一种自己实现它的简单方法:http://www.devcodenote.com/2015/04/collision-free-string-hashing.html
帖子的摘录:
如果我们有一个大写英文字母的字符集,那么字符集的长度是26,其中A可以用数字0表示,B表示数字1,C表示数字2,依此类推,直到Z现在,每当我们想要将这个字符集的字符串映射到一个唯一的数字时,我们就会执行与二进制格式相同的转换
答案 13 :(得分:-3)
CRC-32。谷歌上有大约一万亿个链接。