使用 crypto.generateValues() 生成 0 到 1 的随机数 [英] Generating random numbers 0 to 1 with crypto.generateValues()
问题描述
看起来 Math.random() 生成了一个 [0,1) 范围内的 64 位浮点数,而新的 crypto.getRandomValues() API 只返回整数.使用此 API 在 [0,1) 中生成数字的理想方法是什么?
It looks like Math.random() generates a 64-bit floating point number in the range [0,1) while the new crypto.getRandomValues() API only returns ints. What would be the ideal way to produce a number in [0,1) using this API?
这似乎有效,但似乎不太理想:
This seems to work but seems suboptimal:
ints = new Uint32Array(2)
window.crypto.getRandomValues(ints)
return ints[0] / 0xffffffff * ints[1] / 0xffffffff
澄清一下,我试图产生比 Math.random() 更好的结果.根据我对浮点数的理解,应该有可能获得 52 位随机性的完全随机分数.(?)
To clarify, I am trying to produce better results than Math.random(). From my understanding of floating point, it should be possible to get a fully random fraction for 52 bits of randomness. (?)
编辑 2:为了提供更多背景信息,我并没有尝试做任何加密安全的事情,但是有很多关于 Math.random() 实施不佳的轶事故事(例如 http://devoluk.com/google-chrome-math-random-issue.html) 所以哪里有更好的选择,我想使用它.
EDIT 2: To give a little more background, I'm not trying to do anything cryptographically secure but there are a lot of anecdotal stories about Math.random() being implemented poorly (e.g. http://devoluk.com/google-chrome-math-random-issue.html) so where a better alternative is available I'd like to use it.
推荐答案
记住浮点数只是一个尾数系数,乘以 2 得到一个指数:
Remember that floating point numbers are just a mantissa coefficient, multiplied by 2 raised to an exponent:
floating_point_value = mantissa * (2 ^ exponent)
使用 Math.random
,您可以生成具有 32 位随机尾数并且总是具有 -32
指数的浮点,以便小数位向左移动 32 位,因此尾数永远不会在小数位左侧有任何部分.
With Math.random
, you generate floating points that have a 32-bit random mantissa and always have an exponent of -32
, so that the decimal place is bit shift to the left 32 places, so the mantissa never has any part to the left of the decimal place.
mantissa = 10011000111100111111101000110001 (some random 32-bit int)
mantissa * 2^-32 = 0.10011000111100111111101000110001
尝试运行几次 Math.random().toString(2)
以验证是否确实如此.
Try running Math.random().toString(2)
a few times to verify that this is the case.
解决方案:您可以生成一个随机的 32 位尾数并将其乘以 Math.pow(2,-32)
:
Solution: you can just generate a random 32-bit mantissa and multiply it by 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()
提供给您的原始整数值.
Note that floating points do not have an even distribution (the possible values become sparser the larger the numbers become, due to a lack of precision in the mantissa), making them ill-suited for cryptographic applications or other domains which require very strong random numbers. For that, you should use the raw integer values provided to you by crypto.getRandomValues()
.
JavaScript 中的尾数是 52 位,因此您可以获得 52 位的随机性:
The mantissa in JavaScript is 52 bits, so you could get 52 bits of randomness:
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 以下.
So, all in all, no, this isn't ant shorter than your own solution, but I think it's the best you can hope to do. You must generate 52 random bits, which needs to be built from 32-bit blocks, and then it need to be shifted back down to below 1.
这篇关于使用 crypto.generateValues() 生成 0 到 1 的随机数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!