假设我有很多字符串(比如100亿字符串,每行约50个字符)。我想将字符串分配到10个桶中。每个桶应该容纳大约10%的琴弦。使用哈希函数h(),我可以这样做:
int bucket_for_s = h(s) % 10
然而,这并不能保证分配的均匀性。假设我对所有字符串执行上述操作,并发现30%转到存储桶1,5%转到存储桶2,依此类推。我的问题是:
给定h()分布,有没有办法生成一个新的哈希函数h2(),它将更均匀地分配字符串?
或者,是否有一个进程可以生成一系列哈希函数h2(),h3()...这样1:每个哈希函数都优于前一个和2:我只需要生成一个合理的哈希函数的数量?
我还应该提一下,遗憾的是我不能简单地将输入分成10个部分,因为我的输入分布在几台机器上。我正在寻找一个确定性的解决方案,我可以单独应用于每台机器并获得相同的结果(因此最终“hello”将转到bucket x,无论它存储在哪台机器上。)
答案 0 :(得分:7)
密码固体散列函数应该已经在散列输出的所有位上具有非常均匀的分布。
如果您使用的是Java hashCode()
之类的东西,我认为它似乎是
s [0] * 31 ^(n-1)+ s 1 * 31 ^(n-2)+ ... + s [n-1]
您可能会看到不太理想的哈希分布。
尝试使用SHA-256等加密哈希作为基础。
答案 1 :(得分:6)
链接散列函数或生成一系列散列函数将是不必要的计算上昂贵的。您应该使用已经具有开箱即用所需属性的哈希函数。
可能的候选人
根据您的描述,哈希函数应该是确定性的(您的“问候”示例) - 对于所有哈希函数都是如此 - 并且应该生成均匀分布。
SHA-256之类的加密哈希应该满足您的要求,因为它输出完全不同的哈希值,即使只是略有不同的输入,如“hello”和“hallo”。通过对散列使用模数(%)运算,您可以拥有任意数量的存储桶(当然不超过散列数)。
但是,加密哈希函数是为安全性和校验和而构建的,涉及一些复杂的计算。在您的情况下,您很可能不需要他们提供的与安全相关的强大属性。
您可能更愿意寻找所谓的“非加密哈希函数”,这些函数具有宽松的属性并且更适合查找 - 因此它们针对速度进行了优化。 Java的hashCode(),MurmurHash和已经提到过的CityHash(Google announcement)可能是一个好的开始。
散列函数的确定性与散列的均匀分布
也就是说,由于散列函数对输入是确定性的,因此即使您多次调用散列函数,某个输入的散列“hello”也将始终相同。如果您的数据集包含一些具有大量精确重复的元素(例如“a”和“the”通常是令牌化文本的嫌疑人),则无论您使用哪种散列函数,都很容易导致大小不均匀的桶。 / p>
假设您希望使用均匀分布的哈希来均匀分配工作负载,可以使用以下策略来克服这一点。将每个存储桶视为可由任何可用计算机处理的工作包或作业。如果您拥有的工作包多于计算机(例如,10台计算机可以使用20或30个包),只要您允许灵活的计划,就可以均匀地分配工作负载。当机器A获得一个超大型包装并需要一些时间来处理它时,机器B可以同时处理两个小型或中型包装,从而降低了超大型包装的整体性能影响。
答案 2 :(得分:0)
如何解决它的方向简化为2个桶而不是10个或N个。
假设您获得的分配h()
包含分配p
用于存储分区1,q
用于存储分区2,当然还有p + q = 1
。
现在,我们的目标是找到参数为h2()
的分发版p1, q1, p2, q2
:
给定存储桶1它使用机会p1, q1 (p1+q1=1)
并且给定存储桶2它使用机会p2, q2 (p2+q2=1)
:
h() h2()
/ bucket1 p*p1
bucket1 p -
/ \ bucket2 p*q1
x -
\ / bucket1 q*p2
bucket2 q -
\ bucket2 q*q2
我们的目标是为所有2个桶提供偶然的机会:
p*q1 + q*p2 = 1/2 (total chances for bucket 1 after h2())
p*q2 + q*q2 = 1/2 (total chances for bucket 2 after h2())
和以前一样:
p1 + q1 = 1
p2 + q2 = 1
这是4个方程的线性系统,包含4个变量(分布p1,q1,p2,q2
的参数h2()
)。
注意:对于10个存储桶,我们有h()
p1, p2, ..., p10
p1 + p2 + ... + p10 = 1
。在多少桶的情况下> 2方程式比未知数少:对于p1
这样的每个分配,您得到h2()
p11+p12+...+p1_10=1
的组件。因此,对于10个桶,有h2()
的100个未知参数和仅20个方程。这意味着在求解剩余参数的方程之前,可以给h2()
的80个参数赋予一些任意(但可行)的值。不是很漂亮,但仍然是一个解决方案。
答案 3 :(得分:0)
散列函数旨在产生均匀分布。如果您的数据不是这种情况,那么您的数据会以某种方式“部分”反转该特定哈希函数,并且当您选择其他数据时问题就会消失。
鉴于这是一个理论问题,一种方法是:
您可以使用int bucket_for_s
int bucket_for_s = put_in_bucket(s)
put_in_bucket:
x = h(s) % 10 + 10*((h(s)/10)%10)
if(0<=x<=2) return 0
if(3<=x<=5) return 1
if(6<=x<=9) return 2
#The previous bucket_1 (30%) is now split into 3 buckets
if(10<=x<=27) return 0
#The previous bucket_2 (5%) is now enlarged
#to incorporate more of the small old buckets (or parts of buckets)
#This bucket is now bucket_4
#... more of the same
if(83<=x<=99) return 9
您可以将这个想法扩展到另一个数字,直到您对“决议”感到满意为止
您可以从put_in_bucket
中取出逻辑并使用{{1}}将其放入h2(s)
。
这种方法用于对白噪声(或白化噪声,如本案例中)进行着色,因此得名。