C 简单散列函数
我正在尝试编写一个C程序,该程序使用哈希表存储不同的单词,我需要一些帮助 首先,我创建一个素数大小的哈希表,它与我必须存储的单词数最接近,然后我使用哈希函数为每个单词找到一个地址。 我从最简单的函数开始,将字母相加,结果是88%的冲突。 然后我开始用这个函数做实验,发现不管我把它改成什么,碰撞都不会低于35%。 现在我正在使用C 简单散列函数,c,function,hashtable,string-hashing,C,Function,Hashtable,String Hashing,我正在尝试编写一个C程序,该程序使用哈希表存储不同的单词,我需要一些帮助 首先,我创建一个素数大小的哈希表,它与我必须存储的单词数最接近,然后我使用哈希函数为每个单词找到一个地址。 我从最简单的函数开始,将字母相加,结果是88%的冲突。 然后我开始用这个函数做实验,发现不管我把它改成什么,碰撞都不会低于35%。 现在我正在使用 unsigned int stringToHash(char *word, unsigned int hashTableSize){ unsigned int cou
unsigned int stringToHash(char *word, unsigned int hashTableSize){
unsigned int counter, hashAddress =0;
for (counter =0; word[counter]!='\0'; counter++){
hashAddress = hashAddress*word[counter] + word[counter] + counter;
}
return (hashAddress%hashTableSize);
}
这只是我提出的一个随机函数,但它给了我最好的结果——大约35%的碰撞
在过去的几个小时里,我一直在阅读有关散列函数的文章,我尝试使用一些简单的函数,例如djb2,但所有这些函数都给了我更糟糕的结果。(djb2导致37%的冲突,这并不是更糟,但我期待更好的结果,而不是更糟)
我也不知道如何使用其他一些更复杂的参数,比如杂音2,因为我不知道它们接受的参数是什么(key、len、seed)
即使使用djb2,碰撞率超过35%是正常的,还是我做错了什么?
键、len和seed值是什么?试试sdbm:
hashAddress = 0;
for (counter = 0; word[counter]!='\0'; counter++){
hashAddress = word[counter] + (hashAddress << 6) + (hashAddress << 16) - hashAddress;
}
不过,这只适用于2种尺寸的电源。如果使用模,2个大小的幂只能买到东西,如果散列是一个非常糟糕的散列,具有非常糟糕的“位分布”。错误的位分布通常是由不使用任何类型的位移位(>
或
首先,我创建一个素数大小的哈希表,它与我必须存储的字数非常接近,然后我使用哈希函数为每个字找到一个地址
返回(哈希地址%hashTableSize)
由于不同散列的数量与单词的数量相当,因此您不能期望发生低得多的冲突
我用一个随机散列做了一个简单的统计测试(这是你能达到的最好结果),发现如果你有不同的散列,26%是限制冲突率。。有这么多冲突,你的散列表(即散列的最大有效长度)可能太低,或者您的算法/实现有问题。使用固定表而不是动态表(例如,使用32位或64位哈希作为键的树)有什么具体原因吗?例如,性能?查找复杂性?@Mario我使用的是带有直接链接的动态表,当它太满时会重新刷新自身(或者太空了)。我不使用树的原因是我在做一个专注于哈希表的课程。对于一个具有100%负载因子的最优哈希函数,冲突的预期分数为1/e:=.3678。因此,在这方面,你的函数是最优的。它可能对某些类型的输入敏感(例如带有固定前缀或固定后缀,这在现实生活中非常常见,例如:文件名、URL)没有移位的普通异或往往会导致非常糟糕的哈希。@JonathanLeffler:取决于数据。但在ASCII和逐字符处理的情况下,您可能是对的。将删除此建议。我的函数会给我36,5%的冲突。sdbm-37%djb2-37 Alder32-39%:(谢谢anyway@Hardell请确保您的哈希表足够大。如果哈希表的填充率超过70-80%,则会发生大量冲突!如果要添加100个字,哈希表的大小应至少为130到140,越大越好(例如200)。如果使用模(%
)要将散列裁剪为哈希表大小,正如您所做的那样,不需要2倍大小的幂。仅当您要使用AND(&
)时,才需要2倍大小的幂要裁剪散列,就像和比模运算快好几倍。@Hardell:我想你一定有一个错误的信念,认为好的散列表应该是无冲突的。事实恰恰相反。即使是世界上最伟大的专家编写的最好的散列表实现,在充满现实生活时也会有很多冲突数据。这是绝对不可避免的。每个索引只有两到四个冲突是一个很好的值,我不明白你为什么会抱怨。问题在于你如何处理冲突,而不是如何避免冲突。请参阅我在回答中添加的关于冲突的评论。
hashAddress = 5381;
for (counter = 0; word[counter]!='\0'; counter++){
hashAddress = ((hashAddress << 5) + hashAddress) + word[counter];
}
uint32_t adler32(const void *buf, size_t buflength) {
const uint8_t *buffer = (const uint8_t*)buf;
uint32_t s1 = 1;
uint32_t s2 = 0;
for (size_t n = 0; n < buflength; n++) {
s1 = (s1 + buffer[n]) % 65521;
s2 = (s2 + s1) % 65521;
}
return (s2 << 16) | s1;
}
// ...
hashAddress = adler32(word, strlen(word));
x % 4 == x & 3
x % 8 == x & 7
x % 16 == x & 15
x % 32 == x & 31
...
#include <stdint.h>
#include <stdlib.h>
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
#define mix(a,b,c) \
{ \
a -= c; a ^= rot(c, 4); c += b; \
b -= a; b ^= rot(a, 6); a += c; \
c -= b; c ^= rot(b, 8); b += a; \
a -= c; a ^= rot(c,16); c += b; \
b -= a; b ^= rot(a,19); a += c; \
c -= b; c ^= rot(b, 4); b += a; \
}
#define final(a,b,c) \
{ \
c ^= b; c -= rot(b,14); \
a ^= c; a -= rot(c,11); \
b ^= a; b -= rot(a,25); \
c ^= b; c -= rot(b,16); \
a ^= c; a -= rot(c,4); \
b ^= a; b -= rot(a,14); \
c ^= b; c -= rot(b,24); \
}
uint32_t lookup3 (
const void *key,
size_t length,
uint32_t initval
) {
uint32_t a,b,c;
const uint8_t *k;
const uint32_t *data32Bit;
data32Bit = key;
a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
while (length > 12) {
a += *(data32Bit++);
b += *(data32Bit++);
c += *(data32Bit++);
mix(a,b,c);
length -= 12;
}
k = (const uint8_t *)data32Bit;
switch (length) {
case 12: c += ((uint32_t)k[11])<<24;
case 11: c += ((uint32_t)k[10])<<16;
case 10: c += ((uint32_t)k[9])<<8;
case 9 : c += k[8];
case 8 : b += ((uint32_t)k[7])<<24;
case 7 : b += ((uint32_t)k[6])<<16;
case 6 : b += ((uint32_t)k[5])<<8;
case 5 : b += k[4];
case 4 : a += ((uint32_t)k[3])<<24;
case 3 : a += ((uint32_t)k[2])<<16;
case 2 : a += ((uint32_t)k[1])<<8;
case 1 : a += k[0];
break;
case 0 : return c;
}
final(a,b,c);
return c;
}
unsigned int stringToHash(char *word, unsigned int hashTableSize){
unsigned int initval;
unsigned int hashAddress;
initval = 12345;
hashAddress = lookup3(word, strlen(word), initval);
return (hashAddress%hashTableSize);
// If hashtable is guaranteed to always have a size that is a power of 2,
// replace the line above with the following more effective line:
// return (hashAddress & (hashTableSize - 1));
}