【问题标题】:Improving the distribution of hash function values改进散列函数值的分布
【发布时间】:2012-08-19 13:35:58
【问题描述】:

假设我有大量的字符串(比如 100 亿个字符串,每个字符串约 50 个字符)。我想将字符串分配到正好 10 个桶中。每个桶应容纳大约 10% 的字符串。使用哈希函数 h() 我可以做到:

int bucket_for_s = h(s) % 10

但是,这不能保证分布的均匀性。假设我对所有字符串执行上述操作,发现 30% 进入存储桶 1,5% 进入存储桶 2,依此类推。我的问题是:

给定 h() 分布,有没有办法生成一个新的哈希函数 h2() 来更均匀地分布字符串?

或者,有没有一个过程可以生成一系列哈希函数h2()、h3()……这样1:每个哈希函数都比前一个好,2:我只需要生成一个合理的散列函数的数量?

我还应该提到,不幸的是我不能简单地将输入分成 10 个部分,因为我的输入分布在多台机器上。我正在寻找一种确定性的解决方案,我可以分别应用于每台机器并获得相同的结果(因此最终“hello”将转到存储桶 x,无论它存储在哪台机器上)。

【问题讨论】:

  • 这是一个理论问题吗?或者你有这方面的经验数据吗?另外,您使用的是手工系统还是 Hadoop 之类的系统?
  • 这是我在考虑设计一个手工制作的系统时想到的一个理论问题。到目前为止,我还没有找到答案。

标签: hash bigdata


【解决方案1】:

加密可靠的哈希函数应该已经在哈希输出的所有位上具有非常均匀的分布。

如果您使用的是类似于 Java 的 hashCode(),我认为它看起来像

s[0]*31^(n-1) + s1*31^(n-2) + ... + s[n-1]

您可能会看到不太理想的哈希分布。

尝试使用加密哈希(例如 SHA-256)作为基础。

Google 的City Hash 的分布不如SHA-256,但速度更快。这可能会以更少的计算开销提供足够的分布。

【讨论】:

  • 还需要注意,它强烈依赖于数据。如果您有 500 亿个项目,其中有 50 亿个重复项,那么这 10% 可能会加入存储桶中的其他数据。如果数据真的比散列函数不再重要,那么简单地抓取 10% 并将其放入一个桶中,然后继续可能会更简单。毕竟,与使用传统集合(例如列表)相比,使用存储桶存储 50 亿个项目无法达到目的。
  • @Eric J. - 我只有 10 个桶,所以即使 SHA-256 也可能无法将我的项目均匀地分布在所有项目组中。
  • @pickypg - 我假设字符串的重复次数不会超过一百万次,即输入的 0.01%。不幸的是,我无法将输入轻松分成 10 个部分,因为我没有将所有内容都放在一个地方。
  • 那么您希望如何对它们进行哈希处理以便发送它们进行处理?不要使用该循环来散列(或获取散列),而是使用该循环自己进行分箱:前 10% 进入一台机器/集合/存储桶,然后以此类推。
  • @user1424934:如果你将一个 256 位数字分成 10 等份,你仍然会有近乎完美的分布。最坏的情况是少数桶分配给它们的哈希值少一个。这意味着一些桶有(2^256 / 10)可能的哈希值分配给它们,而其他桶有(2^256 / 10) - 1可能的哈希值。由于 `(2^256 / 10) 是一个非常的大数,因此减去 1 不会产生统计差异。
【解决方案2】:

链接散列函数或生成一系列散列函数会在计算上不必要地昂贵。您应该使用已经具有开箱即用所需属性的哈希函数。

可能的候选人

根据您的描述,哈希函数应该是确定性的(您的“hello”示例)——这适用于所有哈希函数——并且应该生成均匀分布。

SHA-256 等加密散列应该满足您的要求,因为它输出完全不同的散列,即使对于像“hello”和“hallo”这样的略有不同的输入也是如此。通过对哈希使用模 (%) 操作,您可以拥有任意数量的桶(当然不超过哈希的数量)。

但是,加密哈希函数是为安全和校验和而构建的,并且涉及一些复杂的计算。在您的情况下,您很可能不需要它们提供的强大的安全相关属性。

您可能更愿意寻找所谓的“非加密哈希函数”,它们具有宽松的属性并且更适合查找 - 因此它们针对速度进行了优化。 Java 的 hashCode()、MurmurHash 和已经提到的 CityHash (Google announcement) 可能是一个好的开始。

哈希函数的确定性与哈希的均匀分布

也就是说,由于散列函数对于输入是确定性的,因此某个输入作为“hello”的散列将始终相同,即使您多次调用散列函数也是如此。如果您的数据集包含一些具有大量精确重复的元素(例如,“a”和“the”是标记化文本的常见嫌疑人),则无论您使用哪种哈希函数,这很容易导致桶大小不均匀。

假设您希望使用均匀分布的哈希来均匀分布工作负载,可以使用以下策略来克服这一问题。将每个存储桶视为可由任何可用机器处理的工作包或作业。如果您的工作包多于机器(假设 10 台机器有 20 或 30 个包),只要您允许灵活调度,您就可以平均分配工作量。当机器 A 得到一个超大包裹并需要一些时间来处理它时,机器 B 可以同时处理两个中小型包裹,从而降低了超大包裹对整体性能的影响。

【讨论】:

    【解决方案3】:

    关于如何解决它的方向简化为 2 个桶而不是 10 个或 N。

    假设你得到一个分配h(),其中桶1分配p,桶2分配q,当然还有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 个方程的线性系统(分布 h2() 的参数 p1,q1,p2,q2)。

    注意:如果有 10 个存储桶,我们将拥有 h() 和 p1, p2, ..., p10,其中 p1 + p2 + ... + p10 = 1。如果桶数 > 2,则方程少于未知数:对于每个分配,例如 p1,您将获得 h2() 和 p11+p12+...+p1_10=1 的组件。因此,对于 10 个桶,h2() 有 100 个未知参数,只有 20 个方程。这意味着在求解剩余参数的方程之前,可以为h2() 的 80 个参数赋予一些任意(但可行的)值。不漂亮,但仍然是一个解决方案。

    【讨论】:

      【解决方案4】:

      哈希函数旨在产生均匀分布。如果您的数据不是这种情况,那么您的数据在某种程度上是该特定哈希函数的“部分”逆,当您选择其他数据时问题应该会消失。

      鉴于这是一个理论问题,一种方法是:

      美白彩色噪声

      你可以玩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
      

      您可以将这个想法再扩展一个数字,直到您对自己的“决心”感到满意为止

      您可以使用h1(s) 将逻辑从put_in_bucket 中取出并放入h2(s)。

      这种方法用于为白噪声着色(或在本例中为白噪声),因此得名。

      【讨论】:

        猜你喜欢
        • 2019-02-04
        • 2021-06-12
        • 2017-02-08
        • 2011-09-02
        • 2016-09-03
        • 2016-05-04
        • 1970-01-01
        • 2014-01-08
        • 1970-01-01
        相关资源
        最近更新 更多