对于整数的集合(即多集),什么是好的散列函数?

时间:2010-11-14 02:50:05

标签: algorithm hash

我正在寻找一个将多组整数映射到整数的函数,希望能有一些像成对独立的保证。

理想情况下,内存使用量将保持不变,并且可以在插入/删除后的O(1)时间内更新哈希值。 (这样做禁止对整数进行排序并使用哈希函数,如h(x)= h_1(x_1,h_2(x_2,h_3(x_3,x_4)))。)

XORing哈希值不起作用,因为h({1,1,2})= h({2})

如果底层哈希函数具有不切实际的强保证,例如n独立,我认为将模数乘以模数可能有效。

6 个答案:

答案 0 :(得分:5)

我在cstheory.stackexchange.com上问了同样的问题并得到了一个很好的答案:

https://cstheory.stackexchange.com/questions/3390/is-there-a-hash-function-for-a-collection-i-e-multi-set-of-integers-that-has

答案 1 :(得分:2)

反向比特。

例如00001011变为11010000.然后,只需SUM所有反转的设置元素。


如果我们在插入/删除时需要O(1),那么通常的SUM将起作用(这就是用Java实现Sets的方式),尽管不能很好地分布在小整数集上。

如果我们的集合不会均匀分布(通常是这样),我们需要映射N-> f(N),这样f(N)将为预期的数据样本均匀分布。通常,数据样本包含比接近最大数字更接近零的数字。在这种情况下,反向位散列将统一分配它们。

Scala中的示例:

def hash(v: Int): Int = {
        var h = v & 1
        for (i <- 1 to 31) {
                h <<= 1;
                h |= ((v >>> i) & 1)
        }
        h
}
def hash(a: Set[Int]): Int = {
        var h = 0
        for (e: Int <- a) {
                h += hash(e);
        }
        h
}

但是我们的多集的哈希值并不统一,尽管比简单的SUM要好得多。

答案 2 :(得分:2)

我同意Dzmitry使用散列的算术SUM,但我建议使用具有良好输出分布的散列函数用于输入整数,而不是仅仅反转整数中的位。反转位不会改善输出分布。它甚至可以恶化输出分布,因为由于总和溢出而导致高阶位丢失的概率远高于在这种情况下低阶位将丢失的概率。以下是具有良好输出分布的快速哈希函数的示例:http://burtleburtle.net/bob/c/lookup3.c。另请阅读描述必须如何构造散列函数的论文 - http://burtleburtle.net/bob/hash/evahash.html

对集合中的每个元素使用散列值的SUM满足问题中的要求:

  • 内存使用量不变。我们需要为每个集存储一个包含哈希值的普通整数。在从集合中添加/删除元素时,此整数将用于O(1)更新散列。
  • 添加新元素只需要将元素的哈希值添加到现有哈希值,即操作为O(1)。
  • 删除现有元素只需要从现有哈希值中减去元素的哈希值,即操作为O(1)。
  • 对于集合,散列将有所不同,这些集合仅由成对的相同元素组成。

SUM和SUB是面对整数溢出的安全操作,因为它们在modular arithmetic中是可逆的,其中对于java中的整数,模数为2 ^ 32或2 ^ 64。

答案 3 :(得分:0)

Knuth在TAoCP上触及了这一点,这是What integer hash function are good that accepts an integer hash key?的近似重复。

根据您的情况,将您的多组转换为单个整数然后执行链接帖子中描述的哈希可能就是您想要做的。将一个集合变成一个数字是微不足道的;数字串联就可以了。

有关Knuth方法的更多信息,请搜索“Knuth's Multiplicative Method”

-tjw

答案 4 :(得分:0)

Min-hashing应该在这里工作。应用排列,维持n个最小元素的小多数集,选择最大的元素。

阐述:这是在O(1)时空中工作的简单方法。您需要类似优先级队列的东西,而不会使初始值的链接太明显。因此,您可以根据一些精心设计的密钥来命令优先级队列,这相当于在正常排序顺序的排列上运行优先级队列。使队列跟踪多重性,以便所选元素也形成多集。

那就是说,我不确定这种分散是否足够好(并且运行多个排列可能会变得昂贵),所以也许建立在Bradley的答案上。这是一个调整,以便重复的元素不会被取消:

xor(int_hash(x_n, multiplicity_n) foreach n)

答案 5 :(得分:0)

我曾经问过一个类似的问题,“Good hash function for permutations?”,并且得到了一个非常适合我的用例的哈希,我的工作代码中只有很少的冲突。它也可能适合你。计算这样的东西:

// initialize this->hash with 1
unsigned int hash = 1;
void add(int x) {
  this->hash *= (1779033703 + 2*x);
}

因此,无论何时添加数字x,都要使用上面的公式更新哈希码。值的顺序并不重要,您将始终获得相同的哈希值。

如果要合并两个集合,只需将哈希值相乘。

我唯一不确定是否可以删除O(1)中的值。