如果您不熟悉universal hashing,它主要是尝试保证较少数量的碰撞(相反,使用普通的旧模数),使用一些涉及随机性的简单数学。问题是它对我不起作用:
size_t hash_modulo(const int value) {
return (size_t) (value % TABLE_SIZE);
}
// prime 491 is used because its > 128, which is the size of the hash table
size_t hash_universal(const int value) {
const size_t a = (size_t) (rand() % 491 + 1);
const size_t b = (size_t) (rand() % 491);
//printf("a: %zu, b:%zu\n", a, b);
return ((a * value + b) % 491) % TABLE_SIZE;
}
我首先测试模数散列并确定最长的链长(链长意味着散列桶大小):
size_t get_max_chain_length(int input[TABLE_SIZE], size_t (*hash_function)(const int)) {
HashTable *hash_table = hash_table_create(hash_function);
if (!hash_table) {
return 0;
}
for (size_t i = 0; i < TABLE_SIZE; ++i) {
hash_table_add(hash_table, input[i]);
}
size_t maximum_chain_length = 0;
for (int j = 0; j < TABLE_SIZE; ++j) {
const size_t length = length_of_(hash_table->rows[j]);
maximum_chain_length = (length > maximum_chain_length) ? length : maximum_chain_length;
}
//hash_table_print(hash_table);
hash_table_destroy(hash_table);
return maximum_chain_length;
}
我选择其中一个输入导致一个非常大的链(id est一个使用普通模数执行不良)并将其抛向普遍散列。通用散列使用随机性,因此我可以采用恒定输入并仍然获得不同的结果。
这就是问题所在。我尝试了每个大小为128的100个随机输入数组并计算平均最长链和总长链,但两种算法的表现相似。
您可以在我的repo中查看我的主页。
我的问题是:预期结果如何?对于使用模数已经表现不佳的输入,通用散列表现不是更好吗?或者我只是搞砸了我的实施(更有可能)。
提前多多感谢!
答案 0 :(得分:1)
那么,为什么你认为模数不好?如果输入是随机的并且足够大,则模数应该产生均匀分布的结果。统一散列(作为您的链接状态)提供针对非随机(即恶意)输入的保护,这不是这里的情况。
答案 1 :(得分:0)
如果您不熟悉通用哈希,这主要是为了确保冲突次数少……
“尝试保证”并不保证。
…(相对而言,使用普通的旧模数)…
链接的文章说,[使用模数]甚至更简单的哈希函数也是普遍的。
我生成随机输入,然后选择一个使用模数导致大桶的输入。然后,我使用与Universal完全相同的输入来检查结果是否有所改善。并且根据规范,最大链长至少应减少一点。
每个大小为128的100个随机输入数组并不是一个特别大的输入。我从您的仓库中运行了该程序八次;在其中的五次运行中,您的通用哈希将“平均最大链长”减少了约10%;在三次运行中,您的通用哈希将“平均最大链长”增加了类似的数量。我注意到使用通用哈希的最大链长在每次运行中都是恒定的。
总而言之,我们无法保证一种哈希方法总是比另一种更好,并且通用哈希似乎通过不断改进而保持了性能承诺。