哈希函数分布在n个值上(带有扭曲)

时间:2015-11-06 02:13:16

标签: algorithm hash

我想知道是否有任何散列函数可以在n个值上分配输入。分配当然应该是相当统一的。但有一个转折点。如果n的变化很小,则很少有元素会获得新的哈希值。最佳地,它应该在n个值上均匀地分割所有k,并且如果n增加到n + 1,则只有k / n-k /(n + 1)值必须移动到在新散列中均匀分布。显然有一个哈希只是简单地创建统一的值然后修改它将起作用,但这会移动很多哈希来填充新节点。这里的目标是尽可能少的值落入新的桶中。

2 个答案:

答案 0 :(得分:3)

假设2 ^ {n-1}&lt; N <= 2 ^ n。然后有一个标准技巧,用于转换哈希函数H,它产生(至少)n个比特,产生一个从0到N的数字。

  1. 计算H(v)。
  2. 只保留前n位。
  3. 如果小于N,请停止并输出。否则,从顶部开始用H(v)代替v。
  4. 此技术的一些属性:

    • 您可能会担心在某些情况下您必须多次重复循环。但实际上预期的循环次数最多为2。
    • 如果你碰到N并且n不必改变,很少有东西会得到一个新的哈希:只有那些在哈希链中某处有N的东西。 (当然,识别哪些元素具有此属性有点难度 - 通常它可能需要重新运行每个元素!)
    • 如果你碰到N并且n必须改变,大约一半的元素必须被重新包围。但是,这种情况越多,N越大越好 - 每次碰撞都是摊销的 O(1)成本。

    修改以添加关于&#34的其他评论;必须重新发布所有内容&#34;要求:可以考虑将上面的步骤3修改为&#34;从顶部开始,使用H(v)&#34;的前n位;代替。这减少了识别哪些元素需要重新定位的问题 - 因为它们将在桶中用于N的散列 - 尽管我不确定结果散列将具有相当好的冲突避免属性。它肯定会使这个过程变得更加脆弱 - 人们会想要证明H的选择有一些特殊的东西(最后几位不是&#39; t&#34;关键的&#34;它的碰撞避免特性)。

    这是Python中的一个简单示例实现,以及一个简短的main,它显示大多数字符串在正常碰撞时不会移动,并且大约一半的字符串在碰到2 ^ n边界时会被移动。请原谅我代码的任何特质 - Python是一门外语。

    import math
    
    def ilog2(m): return int(math.ceil(math.log(m,2)))
    
    def hash_into(obj, N):
        cur_hash = hash(obj)
        mask = pow(2, ilog2(N)) - 1
        while (cur_hash & mask) >= N:
            # seems Python uses the identity for its hash on integers, which
            # doesn't iterate well; let's use literally any other hash at all
            cur_hash = hash(str(cur_hash))
        return cur_hash & mask
    
    def same_hash(obj, N, N2):
        return hash_into(obj, N) == hash_into(obj, N2)
    
    def bump_stat(objs, N):
        return len([obj for obj in objs if same_hash(obj, N, N+1)])
    
    alphabet = [chr(x) for x in range(ord('a'),ord('z')+1)]
    ascending = alphabet + [c1 + c2 for c1 in alphabet for c2 in alphabet]
    
    def main():
        print len(ascending)
        print bump_stat(ascending, 10)
        print float(bump_stat(ascending, 16))/len(ascending)
    # prints:
    # 702
    # 639
    # 0.555555555556
    

答案 1 :(得分:0)

好吧,当你添加一个节点时,你会希望它填满,所以你实际上需要k /(n + 1)个元素从旧节点移动到新节点。

这很容易实现:

只需像往常一样为每个键生成哈希值。然后,将密钥k分配给[0,N)中的节点:

Let H(k) be the hash of k.

int hash = H(k);
for (int n=N-1;n>0;--n) {
   if ((mix(hash,n) % (i+1))==0) {
      break;
   }
}

//put it in node n

因此,当您添加节点节点1时,它会从节点0中窃取一半的项目。 添加节点2时,它会窃取前2个节点中1/3的项目。 等等...

编辑:添加了mix()函数,以便对每个n不同地混合哈希值 - 否则当n不是素数时会出现非均匀性。