无序多集的hash/crc算法

Algorithm for hash/crc of unordered multiset

本文关键字:crc 算法 hash 无序      更新时间:2023-10-16

假设我想创建一个无序的无符号int的无序多集集。为此,我需要创建一个哈希函数来计算无序多集的哈希。事实上,这对CRC也有好处。

一个显而易见的解决方案是将项目放在向量中,对它们进行排序,并返回结果的散列。这似乎有效,但价格昂贵。

另一种方法是对值进行异或,但很明显,如果我有一个项目两次或没有,结果会是一样的——这是不好的。

我有什么想法可以更便宜地实现这一点吗?我有一个应用程序,它将为数千台和相对较大的设备提供数千台。

由于它是一个多集,您希望相同多集的哈希值相同,这些多集的表示可能具有以不同顺序呈现、添加或删除的相同元素。然后,您希望散列值是可交换的,易于更新,并且可以随元素的每次更改而更改。您还希望两个更改不会轻易取消它们对哈希的影响。

除最后一个条件外,满足所有条件的一个操作是加法。只需对元素求和即可。要使和保持有界,请对哈希值的大小进行模和运算。(例如,64位散列的模264。)要确保插入或删除零值会更改散列,请先在每个值上加一。

总和的一个缺点是两个变化很容易抵消。例如,将1 3替换为2 2。为了解决这个问题,您可以使用相同的方法,对条目的多项式求和,仍然保持交换性。例如,您可以对x2+x+1求和,而不是对x+1进行求和。现在,用相同的总和来设计一组变化变得更加困难了。

这里有一个合理的std::unordered_multiset<int>哈希函数,如果计算是在大素数的基础上进行的话会更好,但这个想法仍然有效。

#include <iostream>
#include <unordered_set>
namespace std {
    template<>
    struct hash<unordered_multiset<int>> {
        typedef unordered_multiset<int> argument_type;
        typedef std::size_t result_type;
        const result_type BASE = static_cast<result_type>(0xA67);
        result_type log_pow(result_type ex) const {
            result_type res = 1;
            result_type base = BASE;
            while (ex > 0) {
                if (ex % 2) {
                    res = res * base;
                }
                base *= base;
                ex /= 2;
            }
            return res;
        }
        result_type operator()(argument_type const & val) const {
            result_type h = 0;
            for (const int& el : val) {
                h += log_pow(el);
            }
            return h;
        }
    };
};
int main() {
    std::unordered_set<std::unordered_multiset<int>> mySet;
    std::unordered_multiset<int> set1{1,2,3,4};
    std::unordered_multiset<int> set2{1,1,2,2,3,3,4,4};
    std::cout << "Hash 1: " << std::hash<std::unordered_multiset<int>>()(set1) 
              << std::endl;
    std::cout << "Hash 2: " << std::hash<std::unordered_multiset<int>>()(set2) 
              << std::endl;
    return 0;
}

输出:

Hash 1: 2290886192
Hash 2: 286805088

当它是素数p时,碰撞次数与1/p成正比。我不确定二次幂的分析是什么。您可以通过在插入/删除整数x时添加/减去BASE^x来高效更新哈希。

将内部multiset实现为值->count哈希映射。

这将使您能够通过以下方式避免偶数元素通过xor抵消的问题:您不用对每个元素进行xor运算,而是根据计数和值构造一个新的数字(例如,将它们相乘),然后可以使用xor构建完整的哈希。