Hash 改进散列函数值的分布
假设我有大量的字符串(比如100亿个字符串,每个大约50个字符)。我想把字符串分为10个桶。每个铲斗应能容纳约10%的绳索。使用散列函数h()可以执行以下操作:Hash 改进散列函数值的分布,hash,bigdata,Hash,Bigdata,假设我有大量的字符串(比如100亿个字符串,每个大约50个字符)。我想把字符串分为10个桶。每个铲斗应能容纳约10%的绳索。使用散列函数h()可以执行以下操作: int bucket_for_s = h(s) % 10 然而,这并不能保证分布的均匀性。假设我对所有字符串执行上述操作,发现30%转到bucket 1,5%转到bucket 2,依此类推。我的问题是: 给定h()分布,是否有方法生成新的哈希函数h2(),使字符串分布更均匀 或者,是否有一个进程可以生成一系列哈希函数h2(),h3()
int bucket_for_s = h(s) % 10
然而,这并不能保证分布的均匀性。假设我对所有字符串执行上述操作,发现30%转到bucket 1,5%转到bucket 2,依此类推。我的问题是:
给定h()分布,是否有方法生成新的哈希函数h2(),使字符串分布更均匀
或者,是否有一个进程可以生成一系列哈希函数h2(),h3()。。。因此,1:每个哈希函数都比前一个好,2:我只需要生成合理数量的哈希函数
我还应该提到,不幸的是,我不能简单地将输入分成10个部分,因为我的输入分布在多台机器上。我正在寻找一种确定性的解决方案,我可以分别应用于每台机器并获得相同的结果(因此最终“hello”将转到bucket x,而不管它存储在哪台机器上)。加密实心散列函数应该已经在散列输出的所有位上具有非常均匀的分布 如果您使用的是类似Java的
hashCode()
,我相信它看起来像
s[0]*31^(n-1)+s*31^(n-2)+…+s[n-1]
您可能会看到一个不太理想的散列分布
尝试使用加密哈希(如SHA-256)作为基础
谷歌的分布不如谷歌,但要快得多。这可能以较少的计算费用提供足够的分布。关于如何求解它的方向简化为2个桶,而不是10个或N个桶 假设您得到一个分配
h()
,分配p
给bucket 1,分配q
给bucket 2,当然分配p+q=1
现在,我们的目标是用参数p1、q1、p2、q2找到这样的分布h2()
:
给定的bucket 1使用机会p1,q1(p1+q1=1)
,给定的bucket 2使用机会p2,q2(p2+q2=1)
:
我们的目标是使所有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
)
注:对于10个桶,我们将有
h()
和p1,p2,…,p10
其中p1+p2+…+p10=1
。当桶数>2时,方程比未知数少:对于像p1
这样的每个分配,您将得到h2()
的一个组件,其中p11+p12+…+p1\u 10=1
)。因此,对于10个铲斗,有100个h2()
的未知参数,只有20个方程式。这意味着在求解剩余参数的方程之前,可以为h2()
的80个参数提供一些任意(但可行)值。虽然不漂亮,但仍然是一个解决方案。链接哈希函数或生成一系列哈希函数会带来不必要的计算开销。您更应该使用一个已经具有开箱即用属性的哈希函数
可能的候选人
根据您所描述的,哈希函数应该是确定性的(您的“hello”示例)-这对于所有哈希函数都是如此-并且应该生成均匀分布
加密散列(如)应该满足您的要求,因为它输出完全不同的散列,即使是对于“hello”和“hallo”等稍微不同的输入也是如此。通过对散列使用模(%)操作,您可以拥有任意数量的bucket(当然不超过散列的数量)
然而,加密哈希函数是为了安全和校验和而构建的,并且涉及一些复杂的计算。在您的情况下,您很可能不需要它们提供的强大的安全相关属性
您可能更希望寻找所谓的“非加密哈希函数”,这些函数具有宽松的属性,并且更适合于查找,因此它们针对速度进行了优化。
Java和前面提到的CityHash()可能是一个好的开始
散列函数的确定性与散列的均匀分布
也就是说,由于散列函数对于输入是确定的,因此作为“hello”的特定输入的散列将始终是相同的,即使您多次调用该散列函数。如果您的数据集包含一些元素,其中包含大量精确的重复项(例如,“a”和“the”通常是标记化文本的可疑项),那么无论您使用哪种哈希函数,这都很容易导致大小不一致的存储桶
假设您希望使用哈希的均匀分布来实现工作负载的均匀分布,那么可以使用以下策略来克服这一问题。将每个铲斗视为可由任何可用机器处理的工作包或作业。如果您的工作包比机器多(假设10台机器有20或30个工作包),那么只要允许灵活的调度,您就可以平均分配工作负载。当机器A得到一个超大的包并需要一些时间来处理它时,机器B可以同时处理两个小型或中型包,从而降低超大包对整体性能的影响。哈希函数旨在产生均匀分布。如果您的数据不是这样,那么您的数据在某种程度上是该特定哈希函数的“部分”反转,当您选择另一个数据时,问题应该消失 鉴于这是一个理论问题,一种方法是: 白化色噪声 您可以使用
int bucket\u进行游戏
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
int bucket_for_s=将_放入_bucket中
将_放入_桶中:
x=h(s)%10+10*((h(s)/10)%10)
如果(0)这是t吗
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