我目前正在尝试在C中为我的程序实现哈希函数。我发现了许多可能的解决方案,但我不理解它们。以下是哈希函数:
int hash(const char *word) {
int hash = 0;
int n;
for (int i = 0; word[i] != '\0'; i++) {
// alphabet case
if (isalpha(word[i]))
n = word[i] - 'a' + 1;
else // comma case
n = 27;
hash = ((hash << 3) + n) % SIZE;
}
return hash;
}
为什么我们会从'a'+1
中减去word[i]
?另外,为什么我们要执行以下操作:hash = ((hash << 3) + n) % SIZE
?
答案 0 :(得分:1)
为什么我们在字符串中添加'a'+ 1?
我们不是...... -
表示减去,而不是添加,而word [i]是字符串的字符,而不是字符串。所以我们减去'a'并在字符串的每个字符上加1。
如果word [i]是小写字母,那么word[i] - 'a' + 1
会计算该字母的数量:'a' - &gt; 1,...'z' - &gt; 26.如果不是小写字母怎么办?好吧,非字母字符(不只是逗号,与注释相反)被映射到27,但大写字母(如果存在)会导致未定义的行为。
“hash =((hash&lt;&lt; 3)+ n)%SIZE”?
这将先前的哈希值乘以8,然后为当前字符添加值1 ... 27,并保证结果不超过SIZE,这可能是哈希桶的数量。如果字符串包含的字符数多于字大小/ 3,则初始字符将被移出。如果SIZE的幂为2且字符串超过SIZE / 3个字符,那么所有这些附加字符都将被移出。
它是如何工作的,但它不是一个非常好的哈希函数。除了具有错误注释且不处理大写字母的代码之外,它也不能很好地处理长字符串,因为如上所述,初始字符将被移出。此外,移位和添加操作以非随机方式组合相邻字符,因此它将产生比最佳值更多的哈希桶冲突。这个哈希函数很快,但有更好的快速哈希函数。有关详细信息,请参阅https://en.wikipedia.org/wiki/Hash_function。
答案 1 :(得分:1)
为什么我们在字符串中添加'a'+ 1?
如果我们不添加“+1”,hash("a") = hash("aa") = has("aaa")
...请检查以下代码
char alpha = 'a';
printf("%d\n", alpha - 'a' + 1); // <= produces '1'
为什么我们要执行以下操作:“hash =((hash&lt;&lt; 3)+ n)%SIZE”?
hash = ((hash * 8) + n ) % SIZE
答案 2 :(得分:1)
为什么我们要在字符串中添加
'a'+1
?
我们没有添加,我们正在减去。而且,我们不会对字符串这样做,我们一次只对一个字符进行处理。
根据作者的意图,这是它的作用:给出a
到z
的字母,表达式产生该字母的序列号:'a'
产生1,{ {1}}生成2,'b'
生成3,依此类推。
不幸的是,这个实现被破坏了:当字母大写时,'c'
返回isalpha
,但表达式的结果不会给你字母编号。实际上,如果您的计算机使用的编码与ASCII码一致,则结果将为负数。
为什么我们要执行以下操作:
true
将哈希的先前值乘以8(乘以3乘以8相同),加上字母的数字,然后通过获得hash = ((hash << 3) + n) % SIZE
除以的余数来限制该值。
由于哈希码的实际值很少,只要它对单词中的小变化很敏感,你就可以使用这个函数:
SIZE
此算法(没有int hash (const char* word)
{
unsigned int hash = 0;
for (int i = 0 ; word[i] != '\0' ; i++)
{
hash = 31*hash + word[i];
}
return hash % SIZE;
}
限制)用于计算Java中SIZE
的哈希码。它非常简单而且效率很高。
答案 3 :(得分:0)
减法是尝试将小写字母转换为1
到26
的数字。逗号转换为27
,但大写字母转换为负值(对于ASCII字符集),这会产生不良副作用。
确实存在潜在的未定义行为:
如果char
类型已签名,则isalpha(word[i])
对于否定char
值的行为未定义。要避免此问题,isalpha
的参数必须转换为unsigned char
:isalpha((unsigned char)word[i])
。
hash = ((hash << 3) + n) % SIZE
也有潜在的未定义行为:左移负值是未定义的行为。如果第一个字符是大写字母,则hash
可以为负值。将hash
和c
的类型更改为unsigned int
以避免这种情况。
表达式hash = ((hash << 3) + n) % SIZE
用于将所有字符的位组合成0
和SIZE-1
之间的值。但请注意,如果SIZE
不是无符号值,则表达式可能会在-SIZE+1
和-1
之间产生负值,这可能会产生不良副作用。
对字符值进行转码并不能真正帮助产生良好的哈希函数。
这是一个更安全的版本:
#include <limits.h>
unsigned int hash(const char *word) {
unsigned int hash = 0, c;
for (size_t i = 0; word[i] != '\0'; i++) {
c = (unsigned char)word[i];
hash = (hash << 3) + (hash >> (sizeof(hash) * CHAR_BIT - 3)) + c;
}
return hash % SIZE;
}