6

看起来 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)所以如果有更好的选择,我想使用它。

4

3 回答 3

7

请记住,浮点数只是尾数系数,乘以 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 以下。

于 2012-12-04T01:42:53.267 回答
0

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

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

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

于 2012-12-04T01:23:07.970 回答
0

这个副本中的比特旋转版本也很好,因为规范说数字是 IEEE 754。此外,请考虑字节序的 DataView 注释。

于 2017-09-19T12:37:29.480 回答