通用散列表现比模数散列更差,出了什么问题?

时间:2016-12-23 09:30:02

标签: c hash hashtable modulo universal-hashing

如果您不熟悉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中查看我的主页。

我的问题是:预期结果如何?对于使用模数已经表现不佳的输入,通用散列表现不是更好吗?或者我只是搞砸了我的实施(更有可能)。

提前多多感谢!

2 个答案:

答案 0 :(得分:1)

那么,为什么你认为模数不好?如果输入是随机的并且足够大,则模数应该产生均匀分布的结果。统一散列(作为您的链接状态)提供针对非随机(即恶意)输入的保护,这不是这里的情况。

答案 1 :(得分:0)

  

如果您不熟悉通用哈希,这主要是为了确保冲突次数少……

“尝试保证”并不保证。

  

…(相对而言,使用普通的旧模数)…

链接的文章说,[使用模数]甚至更简单的哈希函数也是普遍的

  

我生成随机输入,然后选择一个使用模数导致大桶的输入。然后,我使用与Universal完全相同的输入来检查结果是否有所改善。并且根据规范,最大链长至少应减少一点。

每个大小为128的100个随机输入数组并不是一个特别大的输入。我从您的仓库中运行了该程序八次;在其中的五次运行中,您的通用哈希将“平均最大链长”减少了约10%;在三次运行中,您的通用哈希将“平均最大链长”增加了类似的数量。我注意到使用通用哈希的最大链长在每次运行中都是恒定的。

总而言之,我们无法保证一种哈希方法总是比另一种更好,并且通用哈希似乎通过不断改进而保持了性能承诺。