我有很多不相关的命名事物,想要进行快速搜索。 "土豚" 在任何地方都是 "土豚",因此对字符串进行哈希处理并重复使用整数可用于加快比较速度。整个名称集合未知(而且随时间变化)。有什么快速的字符串哈希算法可以生成小的(32或16位)值并具有低碰撞率?
我想看到一个特定于C/C++的优化实现。
我有很多不相关的命名事物,想要进行快速搜索。 "土豚" 在任何地方都是 "土豚",因此对字符串进行哈希处理并重复使用整数可用于加快比较速度。整个名称集合未知(而且随时间变化)。有什么快速的字符串哈希算法可以生成小的(32或16位)值并具有低碰撞率?
我想看到一个特定于C/C++的优化实现。
Murmur Hash 挺不错的。
其中的FNV变体应该能够满足你的需求。它们速度快,输出结果相对均匀。
在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;
}
一个好的主题永远不会晚,我相信人们会对我的发现感兴趣。
我需要一个哈希函数,在阅读了这篇文章并在给出的链接上进行了一些研究之后,我想出了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;
}
这个变种哈希字符串时不考虑大小写的,适用于我的需求,即对用户的登录凭据进行哈希处理。 'clave' 在西班牙语中是 'key' 的意思。很抱歉使用了西班牙语,但那是我的母语,程序也是用它编写的。
我编写了一个程序,可以从'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' 的意思是 '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',实际上会耗尽内存来构建哈希表。下面是输出结果。'No hay memoria para insertar XXXX (insertadas XXX)' 的意思是 '没有足够的内存来插入XXX (已插入XXX)。'基本上在那个点上,malloc()失败了。
无法插入'test_epjcv',因为没有足够的内存(已插入2097701个)。
正在查找冲突...
...共有451个“冲突”字符串...
冲突总数:451 单词总数:2097701
这意味着在2,097,701个字符串中只有451次冲突。请注意,在任何情况下,每个代码中都不会有超过2次冲突。对于我来说,这是一个很好的哈希,因为我需要将登录ID转换为40位唯一ID以进行索引。因此,我使用它将登录凭据转换为32位哈希,并使用额外的8位处理每个代码最多255次冲突。从测试结果来看,这几乎不可能发生。
希望这对某人有用。
编辑:
由于测试框是AIX,我使用LDR_CNTRL=MAXDATA=0x20000000运行它以提供更多内存,并且运行时间更长,结果在此处:
正在查找冲突... 冲突总数:2908 单词总数:5366384
这是在5,366,384次尝试后的2908次冲突!
非常重要:使用-maix64编译程序(因此unsigned long为64位),所有情况下的冲突数都为0!!
h
需要初始化为零,否则你可能会从未初始化的内存开始,并得到不可重现的变化哈希值,这将是相当糟糕的。 - E. T.