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

Sid*_*Sid 3 javascript java random

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

Math.floor(Math.random() * 60000 + 4000);
Run Code Online (Sandbox Code Playgroud)

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

t.8*_*888 8

一个简单的替换Math.random可能如下所示:

/**
 * Return values in the range of [0, 1)
 */
const randomFloat = function () {
  const int = window.crypto.getRandomValues(new Uint32Array(1))[0]
  return int / 2**32
}
Run Code Online (Sandbox Code Playgroud)

要将其扩展到整数:


/**
 * Return integers in the range of [min, max)
 *
 * @todo check that min is <= max.
 */
const randomInt = function (min, max) {
  const range = max - min
  return Math.floor(randomFloat() * range + min)
}
Run Code Online (Sandbox Code Playgroud)

要将其扩展到整数数组:


/**
 * Generate an array of integers in the range of [min, max).
 */
const randomIntArray = function (length, min, max) {
  return new Array(length).fill(0).map(() => randomInt(min, max))
}
Run Code Online (Sandbox Code Playgroud)

生成一个由 0 到 2(含)的 10 个整数组成的数组:

randomIntArray(10, 0, 3)
Run Code Online (Sandbox Code Playgroud)
[0, 2, 1, 2, 0, 0, 1, 0, 1, 0]
Run Code Online (Sandbox Code Playgroud)


phi*_*hag 6

对于给定的最小值和最大值,公式 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 此答案中的函数从范围中排除了“max”,但包括“min”。要解决此问题,请将 `var range = max - min;` 更改为 `var range = max - min + 1;` (3认同)