【问题标题】:Generating random numbers 0 to 1 with crypto.generateValues()使用 crypto.generateValues() 生成 0 到 1 的随机数
【发布时间】:2012-11-21 14:10:00
【问题描述】:

看起来 Math.random() 在 [0,1) 范围内生成一个 64 位浮点数,而新的 crypto.getRandomValues() API 只返回整数。使用此 API 在 [0,1) 中生成数字的理想方法是什么?

这似乎可行,但似乎不是最理想的:

ints = new Uint32Array(2)
window.crypto.getRandomValues(ints)
return ints[0] / 0xffffffff * ints[1] / 0xffffffff

编辑:澄清一下,我试图产生比 Math.random()更好的结果。根据我对浮点的理解,应该可以获得 52 位随机性的完全随机分数。 (?)

编辑 2:为了提供更多背景信息,我并没有尝试做任何加密安全的事情,但是有很多关于 Math.random() 实施不佳的轶事故事(例如 http://devoluk.com/google-chrome-math-random-issue.html)所以哪里有更好的有替代品我想用它。

【问题讨论】:

  • 我在 Chrome 中试过你的代码,我也得到了值> 1,例如2.5....
  • 为什么是Math.abs()?从我在 Chrome 中看到的情况来看,随机数是正数。
  • @ŠimeVidas 实际上,它们必须是正数,因为 OP 使用的是 Uint(无符号整数)缓冲区视图。 crypto.getRandomValues 只是生成一个随机二进制缓冲区,无论使用何种视图,都会将其上下文化为数字。
  • @apsillers 我明白了。我尝试了Int32Array,确实得到了负数。
  • 为什么是这样的表达方式?不只是ints[0] / 0xffffffff 会产生一个随机的[0,1) 值吗?

标签: javascript random


【解决方案1】:

好吧,如果您确实需要 [0,1) 范围内的数字,这应该是最佳选择。

该代码的问题在于不同数字的几率不再相同。

使用该代码更有可能获得例如 0.5 (1*0.5,0.5*1,0.75*0.666) 而不是 1 (1*1)。

【讨论】:

  • @ŠimeVidas 确实如此;我修复了错误的示例代码,但我认为规范很明确。
【解决方案2】:

请记住,浮点数只是一个尾数系数,乘以2,得到一个指数

floating_point_value = mantissa * (2 ^ exponent)

使用Math.random,您可以生成具有 32 位随机尾数的浮点数,并且始终 的指数为 -32,因此小数点向左移动 32 位,所以尾数永远不会在小数点的左边有任何部分。

mantissa =         10011000111100111111101000110001 (some random 32-bit int)
mantissa * 2^-32 = 0.10011000111100111111101000110001

尝试运行Math.random().toString(2) 几次以验证是否是这种情况。

解决方案:你可以生成一个随机的 32 位尾数并乘以 Math.pow(2,-32)

var arr = new Uint32Array(1);
crypto.getRandomValues(arr);
var result = arr[0] * Math.pow(2,-32);
// or just   arr[0] * (0xffffffff + 1);

注意浮点不具有均匀分布(由于尾数缺乏精确度,数字越大,可能的值越稀疏),使其不适合加密需要非常强的随机数的应用程序或其他领域。为此,您应该使用crypto.getRandomValues() 提供给您的原始整数值。

编辑:

JavaScript 中的尾数是 52 位,所以你可以得到 52 位的随机性:

var arr = new Uint32Array(2);
crypto.getRandomValues(arr);

// keep all 32 bits of the the first, top 20 of the second for 52 random bits
var mantissa = (arr[0] * Math.pow(2,20)) + (arr[1] >>> 12)

// shift all 52 bits to the right of the decimal point
var result = mantissa * Math.pow(2,-52);

所以,总而言之,不,这并不比您自己的解决方案短,但我认为这是您希望做的最好的事情。必须生成 52 个随机位,需要从 32 位块构建,然后需要向下移回 1 以下。

【讨论】:

  • 相当于var result = arr[0] / ( 0xffffffff + 1 );,对吧?
  • 我应该澄清一下,我正在寻找与 Math.random() 相同但更好的接口,即 52 位随机性。
  • @ŠimeVidas 是的,这也比每次都计算Math.pow 要好。谢谢,添加到我的答案中。
  • @svachalek 编辑了一个解决方案,但恐怕不会比你自己的短!
  • 我认为这样更好,因为没有随机位相互相乘;我不是一个数学家,但这让我觉得以某种方式减少了随机性。我认为在 Math.pow 上使用常量可能更快: (ints[0] * 0x100000 + (ints[1] & 0xfffff)) / 0xfffffffffffff
【解决方案3】:

bit-twiddling version in this duplicate 也不错,因为the spec says that numbers are IEEE 754。另外,请考虑 DataView 注释的字节顺序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-11
    • 2017-05-28
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 2014-10-17
    • 1970-01-01
    相关资源
    最近更新 更多