为什么Golang的MD5发布看起来并不统一?

时间:2018-04-29 07:06:50

标签: go hash md5

我完全相信我在某处有错误或误解了某些内容,但为什么以下代码似乎没有显示出均匀分布?

func TestMD5(t *testing.T) {
    n := 50000
    counts := map[uint32]int{} // # of hashes per 1/nth shard

    for i := 0; i < n; i++ {
        hash := md5.Sum(newUUID())
        result := binary.BigEndian.Uint32(hash[:4])
        counts[result/uint32(n)]++
    }

    dupeShards := 0
    dupeEntries := 0
    for _, count := range counts {
        if count > 1 {
            dupeShards++
            dupeEntries += count - 1
        }
    }
    t.Logf("%d inputs hashed to the same %d shards as other inputs.", dupeEntries, dupeShards)

    if len(counts) < n*95/100 {
        t.Fatalf("%d populated shards not within 5%% of expected %d uniform distribution!", len(counts), n)
    }
}

https://play.golang.org/p/05mA0Dl9GBG

-

代码说明:

  • MD5 50k随机UUID。
  • 对于每个MD5总和,取​​前4个字节并转换为uint32。
  • 将结果除以50k(使用截断/底部除法)将散列分布为50k均匀间隔的碎片。

==&GT;我希望50k MD5总和可以在50k分片中均匀分布,但我一直看到只有~38k的分片,并且在~10k的分片中聚集在一起:

main.go:29: 12075 inputs hashed to the same 9921 shards as other inputs.
main.go:32: 37925 populated shards not within 5% of expected 50000 uniform distribution!

我也可以用其他哈希来重复这个(例如FNV),所以我猜我在误解某些东西。谢谢你的帮助!

1 个答案:

答案 0 :(得分:6)

这是绝对正常的行为,并没有显示MD5实现的任何偏见或不正确。

你正在做的是(非常接近)在0到49,999之间抽取50,000个随机数。当你这样做时,几乎可以肯定会重复许多数字,因此有些数字不会出现。实际上,50,000个数字应该完全不同,绝对没有重复,这是不太可能的。

你可以用一个六面骰子来测试 - 如果你扔了6次,你就不太可能得到所有六个数字,更有可能看到它们中的3,4或5个,一个,两次或三次重复。它也与所谓的birthday paradox有关。

这种现象的另一个例子是'Panini贴纸问题'。帕尼尼贴纸专辑是一本书,可以容纳600个足球贴纸,用于纪念世界杯足球赛。每个都是编号和不同的,并且它们以包的形式随机出现。你必须得到每个号码中的一个来完成专辑。假设您购买了正确数量的贴纸以填充相册。如果你能够完美地填充相册,没有任何双打或缺少贴纸,那将是非常幸运的。事实上,你必须平均购买贴纸数量的大部分,以便至少获得其中一个(如果你不与其他收藏家交换重复)。

出现的不同值0-49,999的数量和显示“聚集”的数量可以用数学方法计算。我不确定你是如何测量丛生的。但是,即使您看到的实际值会发生变化,38K填充值的值也会在一次试验中保持稳定。

实际上,预期填充值的数量是(1 - 1 / e)n,其中n是可能值的数量,e是数学常数2.718281828 ... n = 50000的答案是31606。当然,你不会总是得到这个值,但所有结果都应该在几百左右(这里是spitballing)。你在你的程序中犯了一个小错误,所以我无法破译给你~37000的相关计算。