散列函数不会给出欲望结果

时间:2015-08-05 07:36:13

标签: c hash

我正在实现哈希函数以检查字谜,但我没有获得所需的输出。你能说出出了什么问题吗?

输出:

key[148]:val[joy]
key[174]:val[jam]
key[294]:val[paula]
key[13]:val[ulrich]
key[174]:val[cat]
key[174]:val[act]
key[148]:val[yoj]
key[265]:val[vij]
key[265]:val[jiv]

此处键值174适用于字符串actcat(字谜),但jam不能达到相同的效果。

以下是代码段。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

unsigned long hash(char *str, size_t size) {
    unsigned long hash_val = 5381;
    unsigned long sum = 0;
    char *val;
    int i, j;
    for (j = 0; j < 9; j++) {
        val = malloc(strlen(str) + 1);
        memset(val, '\0', strlen(str) + 1);
        strcpy(val, str);

        for (i = 0; val[i] != '\0'; i++) {
            sum = sum + val[i];
        }
        return size % sum;
    }
}

int main() {
   int i;
   char *str[9] = { "joy", "jam", "paula", "ulrich","cat", "act","yoj", "vij", "jiv" };
   unsigned long key;
   size_t size = 4542; // it may be anything just for test it is being used
   for (i = 0; i < 9; i++) {
        key = hash(str[i], size);
        printf("\nkey[%ld]:val[%s]", key, str[i]);
    }
    return 1;
}

3 个答案:

答案 0 :(得分:3)

是的,它可以,因为您的哈希函数编写得非常糟糕 - 它返回所有字符串字符的常量'size'变量模和。

问题是ASCII码'c'+'a'+'t'的总和等于'j'+'a'+'m'(等于312)所以你得到的值相同为你的'哈希'。

您可以为anagram表使用“普通”(例如多项式)哈希函数,但使用排序字符串 - 这将是最简单的方法。

对于另一种方法,您可以计算字符串中每个字母的出现次数(直方图)和散列(或只是按原样存储)。

我建议你对这个主题做一些研究,因为这是一个非常常见的任务。

此外,您可以对字符串进行排序,让unordered_set<string>为您完成工作。

答案 1 :(得分:2)

  

但是果酱可能会出现同样的情况。

好吧,你出错了。让我们看看你的算法。你正在做的是基本上总结字符串元素的ASCII值,并返回相对于总和的固定值的模数结果。

根据ASCII table详细说明,

j == 106
a == 97
m == 109

c == 99
a == 97
t == 116

这两个词最终的总和结果为312。 现在按你的算法,

 4542 % 312

假设给出常量值,对吗?这就是它所给予的。

现在,不要"sad"

s == 115
a ==97
d == 100

也提出了312

那就是说,我看到你的函数中定义了一个局部变量unsigned long hash_val = 5381;,但没有使用它。

答案 2 :(得分:0)

您的哈希函数存在许多问题:

  • for (j = 0; j < 9; j++)循环完全没用。
  • 为字符串的副本分配内存并忘记释放它是完全不够的!只需直接使用字符串。
  • 你总结方法有太多简单的碰撞,因为你诊断出:anagrams产生相同的总和,但也有很多简单的单词。您应该在添加每个字符值之前对其进行混洗。
  • return size % sum;应该是return sum % size;,因此返回值可以用作大小为size的哈希表的索引。事实上,如果size % sum碰巧计算到sum0会调用未定义的行为,这需要一个非常长的字符串(> 16MB),但这是可能的。

这是一个改进的哈希函数:

#include <limits.h>

// constraints: str != NULL, size > 0
size_t hash(const char *str, size_t size) {
    size_t sum = 5381;  // initial salt
    while (*str != '\0') {
        // rotate the current sum 2 places to the left
        sum = (sum << 2) | (sum >> (CHAR_BIT * sizeof(sum) - 2));
        // add the next character value
        sum += (unsigned char)*str++;
    }
    return sum % size;
}