我想快速搜索许多无关的命名事物。“ aardvark”在任何地方都始终是“ aardvark”,因此对字符串进行哈希处理并重新使用整数可以很好地加快比较速度。整个名称集是未知的(并且会随时间变化)。什么是快速字符串哈希算法,它将生成较小的(32或16)位值并且具有较低的冲突率?
我希望看到针对C / C ++的优化实现。
我想快速搜索许多无关的命名事物。“ aardvark”在任何地方都始终是“ aardvark”,因此对字符串进行哈希处理并重新使用整数可以很好地加快比较速度。整个名称集是未知的(并且会随时间变化)。什么是快速字符串哈希算法,它将生成较小的(32或16)位值并且具有较低的冲突率?
我希望看到针对C / C ++的优化实现。
Answers:
其中的FNV变种应该满足你的要求。它们速度很快,并产生相当均匀的分布式输出。
Murmur Hash非常不错。
在eternallyconfuzzled.com上也有一篇不错的文章。
詹金斯(Jenkins)的一次性字符串哈希应如下所示:
#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;
}
根据您的用例,可能会更好的另一种解决方案是interstrings。这就是符号在Lisp中的工作方式。
插入字符串是一个字符串对象,其值是实际字符串字节的地址。因此,您可以通过检入一个全局表来创建一个嵌入的字符串对象:如果该字符串在其中,则可以将嵌入的字符串初始化为该字符串的地址。如果不是,则将其插入,然后初始化您的实习字符串。
这意味着从同一字符串构建的两个内部字符串将具有相同的值,即地址。因此,如果N是系统中实习字符串的数量,则特征为:
干杯,
卡尔
您为什么不只使用Boost库?它们的哈希函数易于使用,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上找到boost
对于一个好的学科来说永远不会迟到,我相信人们会对我的发现感兴趣。
我需要一个哈希函数,在阅读了这篇文章并对此处给出的链接进行了一些研究之后,我想到了丹尼尔·J·伯恩斯坦算法的这种变体,我曾经做过一个有趣的测试:
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等。
这是测试的输出-“ Colision entre XXX y XXX”表示“ XXX和XXX的碰撞”。“ palabras”表示两种语言中的“单词”和“总计”相同。
Buscando碰撞... 碰撞入口'test_phiz@hotmail.com'和'test_juxg@cloud-nueve.com'(1DB903B7) 碰撞入口'test_rfhh@hotmail.com'和'test_fpgo@yahoo.com'(2F5BC088) 碰撞入口'test_wxuj@hotmail.com'和'test_pugy@cloud-nueve.com'(51FD09CC) 碰撞入口'test_sctb@gmail.com'和'test_iohw@cloud-nueve.com'(52F5480E) 碰撞入口'test_wpgu@cloud-nueve.com'和'test_seik@yahoo.com'(74FF72E2) 碰撞入口'test_rfll@hotmail.com'和'test_btgo@yahoo.com'(7FD70008) 碰撞入口'test_wcho@cloud-nueve.com'和'test_scfz@gmail.com'(9BD351C4) 碰撞入口'test_swky@cloud-nueve.com'和'test_fqpn@gmail.com'(A86953E1) 碰撞入口'test_rftd@hotmail.com'和'test_jlgo@yahoo.com'(BA6B0718) 碰撞输入'test_rfpp@hotmail.com'和'test_nxgo@yahoo.com'(D0523F88) 碰撞入口'test_zlgo@yahoo.com'和'test_rfdd@hotmail.com'(DEE08108) Collisiones总数:11 巴拉布拉斯总数:456976
不错,在456,976个碰撞中有11个碰撞(当然,使用完整的32位作为表长度)。
使用5个字符(即从“ test_aaaaa”到“ test_zzzzz”)运行程序实际上会耗尽构建表的内存。以下是输出。“没有干草记忆插入XXXX(插入XXX)”表示“没有剩余空间可插入XXX(插入XXX)”。基本上,malloc()在那时失败了。
没有干草记忆插入器'test_epjcv'(insertadas 2097701)。 Buscando碰撞... 451个'collision'字符串... 冲突总数:451 巴拉布拉斯总数:2097701
这意味着2,097,701个字符串上只有451次碰撞。请注意,在任何情况下,每个代码都不会发生2次以上的冲突。我确认这对我来说是一个很好的哈希值,因为我需要将登录ID转换为40位唯一ID以进行索引。因此,我将其用于将登录凭据转换为32位哈希,并使用额外的8位来处理每个代码最多255个冲突,这几乎不可能生成测试结果。
希望这对某人有用。
编辑:
就像测试框是AIX一样,我使用LDR_CNTRL = MAXDATA = 0x20000000运行它以为其提供更多内存,并且运行时间更长,结果在这里:
Buscando Colisiones ...总数:2908总数:Palabras:5366384
经过5,366,384次尝试,即为2908!
非常重要:使用-maix64编译程序(因此unsigned long为64位),在所有情况下冲突次数均为0!
鲍勃·詹金斯(Bob Jenkins)有许多可用的哈希函数,这些哈希函数速度很快且冲突率较低。
这里描述了一种自己实现的简单方法:http : //www.devcodenote.com/2015/04/collision-free-string-hashing.html
帖子摘录:
如果说我们有一个大写英文字母的字符集,则字符集的长度为26,其中A可以由数字0表示,B可以由数字1表示,C可以由数字2表示,依此类推,直到Z由数字表示25.现在,每当我们想要将此字符集的字符串映射到唯一数字时,我们都会执行与二进制格式相同的转换
(32 or 16) bit values
给定字符集(例如,从24到1.111.998个代码点),它(给定显示链接内容的超文本浏览器)如何映射到?基本转换不是有用的哈希函数。
CRC-32。谷歌上有大约一万亿个链接。