【问题标题】:How to generate a random number between (1-100) from MD5 hash如何从 MD5 哈希生成(1-100)之间的随机数
【发布时间】:2020-11-02 18:04:20
【问题描述】:

简单地说,我想从 MD5 哈希中生成一个介于 1-100 之间的随机数。

我将始终使用电子邮件字符串作为参数。

原因是我想创建一个简单的 a/b 测试来根据返回的数字确定特定的电子邮件布局。

这是我的解决方案,但我不知道它是否准确,坦率地说,我不完全确定我是否知道自己在做什么..

parseInt(crypto.createHash('md5').update('example@gmail.com').digest("hex"), 16) % 10**2

52 is returned

谁能引导我朝着正确的方向前进并详细解释正在发生的事情?有没有更好的办法?

谢谢。

【问题讨论】:

  • 您希望相同的 MD5 哈希值始终产生相同的“随机”数字?
  • 如何定义“准确”?
  • @RobertHarvey 我认为上面的评论会回答你的问题。
  • 我认为您应该更改标题。您似乎并不真正想要一个“随机”数字,但您想通过使用哈希以伪随机方式将确定性值分配给电子邮件地址,对吗?您正在使用哈希来“打乱”电子邮件地址,这样您就不会使用相同的设置聚集相似的电子邮件地址,对吧?

标签: javascript node.js encryption md5 sha256


【解决方案1】:

JavaScript 中的Numbers 只是 64 位浮点数,因此仅适用于大约 15 位十进制数字。取约 37 个十进制数字的模数将意味着低位有效地为零,并且您将获得相对稀疏的输出。例如:

a = Array(100).fill(0)
for (i = 0; i < 10000; i++) {
  d = Math.random() * 2**128
  a[d % 100] += 1
}

请注意,Math.random() * 2**128 大致相当于生成随机电子邮件的哈希值。这给了我一个a 之类的:

[
  409, 0, 0, 0, 408, 0, 0, 0, 408, 0, 0, 0,
  398, 0, 0, 0, 420, 0, 0, 0, 434, 0, 0, 0,
  356, 0, 0, 0, 401, 0, 0, 0, 398, 0, 0, 0,
  423, 0, 0, 0, 346, 0, 0, 0, 397, 0, 0, 0,
  406, 0, 0, 0, 378, 0, 0, 0, 429, 0, 0, 0,
  410, 0, 0, 0, 421, 0, 0, 0, 358, 0, 0, 0,
  389, 0, 0, 0, 363, 0, 0, 0, 398, 0, 0, 0,
  398, 0, 0, 0, 426, 0, 0, 0, 396, 0, 0, 0,
  430, 0, 0, 0
]

表示只能被 4 整除的值是可能的,因此您的 100 个值中的 75 个永远不会被使用。

正如 James K. Polk 所评论的,取模数也略有偏差,但上述问题要大得多。我还支持使用除法的建议,因为这可以保留高位并保持熵,例如:

digest = crypto.createHash('md5').update('example@gmail.com').digest("hex")
Math.floor(parseInt(digest, 16) / 2**128 * 100)

您可以使用类似于上述循环的方法来查看输出的均匀分布

请注意,以上两个都生成从 0 到 99 的值,因此您可能希望将 1 添加到结果中

另一种方法是使用 Node 的 BigInt 类型,类似于:

digest = crypto.createHash('md5').update('example@gmail.com').digest()
Number(digest.readBigUInt64BE() / (2n**64n / 100n + 1n))

这避免了转换为字符串并再次返回,但得到的答案基本相同。

【讨论】:

    【解决方案2】:

    您的第一部分是创建 MD5 哈希:

    crypto.createHash('md5').update('example@gmail.com').digest("hex")
    // e820bb4aba5ad74c5a6ff1aca16641f6
    

    产生的 md5 哈希是一个 32 位的十六进制数。 parseInt(hash, 16) 将其解析为整数。这对于整数来说太大了,所以你得到一个很大的浮点数。我不确定该转换到底发生了什么。然后取模 100,得到 0 到 99 的值。

    如果您取 md5 的前两位数字并获得一个 0-255 之间的数字并以此为基础进行设置,那么推理会更容易一些。

    【讨论】:

    • 感谢您的回复,为什么在 0-255 之间?你能举个例子,我会把你的答案标记为正确,因为它最接近我想要的。
    • 因为您正在获取散列的第一个字节。该字节的十六进制值为00到FF,十进制为0到255
    【解决方案3】:

    如果您使用库 seedrandom,您可以为数字生成器播种。
    播种数字生成器可确保从“零”开始时始终以相同的顺序生成数字。
    它在自动生成的迷宫和游戏关卡中非常棒(想想我的世界中的关卡种子)

    使用此库,您可以执行以下操作以获得一致的结果。

    var myrng = new Math.seedrandom('example@example.com');
    console.log("int 1: " + myrng.int32());
    console.log("int 2: " + myrng.int32());
    console.log("int 3: " + myrng.int32());
    console.log("int 4: " + myrng.int32());
    console.log('round 2, repeating the above with same seed');
    myrng = new Math.seedrandom('example@example.com');
    console.log("int 1: " + myrng.int32());
    console.log("int 2: " + myrng.int32());
    console.log("int 3: " + myrng.int32());
    console.log("int 4: " + myrng.int32());
    console.log('round 3, repeating the above with a different seed');
    myrng = new Math.seedrandom('#example@example.com');
    console.log("int 1: " + myrng.int32());
    console.log("int 2: " + myrng.int32());
    console.log("int 3: " + myrng.int32());
    console.log("int 4: " + myrng.int32());
    <script src="//cdnjs.cloudflare.com/ajax/libs/seedrandom/3.0.5/seedrandom.min.js">
    </script>

    【讨论】:

    • 很好的答案,我希望将字符串分配到几个“桶”中,或多或少地分布均匀,seedrandom 似乎是一个完美的工具。
    猜你喜欢
    • 2011-08-17
    • 2018-08-24
    • 2016-09-25
    • 2018-12-10
    • 2011-01-23
    • 1970-01-01
    • 2013-09-08
    相关资源
    最近更新 更多