如何将哈希统一投影到固定数量的桶中

机器算法验证 均匀分布
2022-02-10 23:07:25

各位统计学家,您好,

我有一个生成散列的源(例如,计算带有时间戳和其他信息的字符串并使用 md5 散列),我想将它投影到固定数量的桶中(比如 100)。

样本哈希:0fb916f0b174c66fd35ef078d861a367

我最初的想法是只使用哈希的第一个字符来选择一个桶,但这会导致非常不均匀的投影(即一些字母很少出现,而另一些则非常频繁)

然后,我尝试使用 char 值的总和将此六进制字符串转换为整数,然后取模选择一个存储桶:

import sys

for line in sys.stdin:
    i = 0
    for c in line:
        i += ord(c)
    print i%100

它似乎在实践中有效,但我不知道是否有任何常识或理论结果可以解释为什么以及在多大程度上这是正确的?

[编辑] 经过一番思考,我得出以下结论:理论上,您可以通过将散列解释为数字来将散列转换为(非常大的)整数: i = h[0] + 16*h[1]+16* 16*h[2] ... + 16^31*h[31] (每个字母代表一个十六进制数)。然后你可以对这个大数字取模以将其投影到存储桶空间。[/编辑]

谢谢 !

3个回答

注意:将讨论中出现的答案放在评论中,以便感兴趣的人更容易阅读

(更新后的版本)

假设我们有一个生成独立事件的源,我们希望将这些事件均匀地分配到个桶中。B

关键步骤是:

  1. 散列为大小为的整数ei2N
  2. 投影到 asR×[0,1[p=i2N
  3. 找到匹配的桶使得bibiBp<bi+1B

对于 1. 一个流行的解决方案是使用MurmurHash生成一个 64 位或 128 位整数。

对于 3. 一个简单的解决方案是迭代并检查是否在j=1..Bp[bjB,bj+1B[

在(python)伪代码中,整个过程可能是:

def hash_to_bucket(e, B):
    i = murmurhash3.to_long128(str(e))
    p = i / float(2**128)
    for j in range(0, B):
        if j/float(B) <= p and (j+1)/float(B) > p:
            return j+1
    return B

(以前的版本,真的不是最优的)

第一个观察是哈希的第n个字母应该相对于字母表均匀分布(这里是 16 个字母长 - 感谢@leonbloy 指出)。

然后,要将其投影到 [0,100[ 范围,诀窍是从散列中获取 2 个字母(例如,第 1 和第 2 位置)并生成一个整数:

int_value = int(hash[0])+16*int(hash[1])

该值位于 [0,16+(16-1)*16[ 范围内,因此我们只需将其取为 100 即可生成 [0, 100[ 范围内的存储桶: 正如评论中指出的那样,做所以影响分布的均匀性,因为第一个字母比第二个字母更有影响力。

bucket = int_value % 100

理论上,您可以将整个哈希转换为(非常大的)整数,方法是将其解释为数字: i = h[0] + 16*h[1]+16*16*h[2] ... + 16^ 31*h[31](每个字母代表一个十六进制数)。然后你可以对这个大数字取模以将其投影到存储桶空间。然后可以注意到,取 i 的模可以分解为分配和加法运算:

imodN=((h0modN)+(16modN×h1modN)+...+(1631modN×h31modN))modN

我遇到了类似的问题,并想出了一个不同的解决方案,它可以更快、更容易地用任何语言实现。

我的第一个想法是在固定数量的桶中快速统一地调度项目,并且为了可扩展,我应该模仿随机性。

所以我编写了这个小函数,在 [0, 1[ 中返回一个浮点数,给定一个字符串(或实际上任何类型的数据)。

在 Python 中:

import math
def pseudo_random_checksum(s, precision=10000):
    x = sum([ord(c) * math.sin(i + 1) for i,c in enumerate(s)]) * precision
    return x - math.floor(x)

当然它不是随机的,事实上它甚至不是伪随机的,相同的数据总是会返回相同的校验和。但它的行为就像随机的,而且非常快。

您只需将每个项目分配给存储桶编号 math.floor(N * pseudo_random_checksum(item)) 即可轻松分派和稍后检索 N 个存储桶中的项目。

在这里,您可以找到适用于按位运算的无分支统一存储桶分布。