C中字符串的哈希函数

时间:2013-12-09 03:51:56

标签: c hash hashcode

我目前正在尝试在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

4 个答案:

答案 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

我们没有添加,我们正在减去。而且,我们不会对字符串这样做,我们一次只对一个字符进行处理。

根据作者的意图,这是它的作用:给出az的字母,表达式产生该字母的序列号:'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)

减法是尝试将小写字母转换为126的数字。逗号转换为27,但大写字母转换为负值(对于ASCII字符集),这会产生不良副作用。

确实存在潜在的未定义行为:

  • 如果char类型已签名,则isalpha(word[i])对于否定char值的行为未定义。要避免此问题,isalpha的参数必须转换为unsigned charisalpha((unsigned char)word[i])

  • hash = ((hash << 3) + n) % SIZE也有潜在的未定义行为:左移负值是未定义的行为。如果第一个字符是大写字母,则hash可以为负值。将hashc的类型更改为unsigned int以避免这种情况。

表达式hash = ((hash << 3) + n) % SIZE用于将所有字符的位组合成0SIZE-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;
}
相关问题