如何使用window.crypto.getRandomValues获取特定范围内的随机值

我们一直在使用Math.random获取4000-64000之间的随机数:

Math.floor(Math.random() * 60000 + 4000); 

我们现在必须使用更加加密的安全随机数生成器来替换它。 在搜索了这个问题后,我们决定使用window.crypto.getRandomValues。 我无法弄清楚如何使用它来获取特定范围之间的随机数。 有人可以帮忙吗?

对于给定的最小值和最大值,公式 u \ cdot \ left(1  -  {2 ^ u \ boldsymbol {\ textup {mod}}(max-min)\ over 2 ^ u} \ right)\ sum_ {i = 0} ^ {\ infty} \ left( 2 ^ u \ boldsymbol {\ textup {mod}}(max-min)\ over 2 ^ u \ right)^ i(i + 1) 如果您一次请求u位,则描述平均使用多少位,如果返回结果则重试会引入偏差。

幸运的是,最佳策略是一次性请求ceil(log2(max - min + 1))位。 无论如何我们只能使用crypto.getRandomValues获取完整的字节,所以如果我们每个函数调用都有一次crypto.getRandomValues调用,我们能做的最好是:

 // Generate a random integer r with equal chance in min <= r < max. function randrange(min, max) { var range = max - min; if (range <= 0) { throw new Exception('max must be larger than min'); } var requestBytes = Math.ceil(Math.log2(range) / 8); if (!requestBytes) { // No randomness required return min; } var maxNum = Math.pow(256, requestBytes); var ar = new Uint8Array(requestBytes); while (true) { window.crypto.getRandomValues(ar); var val = 0; for (var i = 0;i < requestBytes;i++) { val = (val << 8) + ar[i]; } if (val < maxNum - maxNum % range) { return min + (val % range); } } } 

如果生成许多值,则可以考虑进行一些优化,即提前请求更多字节(即更大的数组)。 如果你的范围变小(比如你要翻硬币),那么以一种基于位的方式工作也许是有益的,即预先请求许多位然后只用掉你真正需要的随机位。