简体   繁体   English

Javascript:使用 crypto.getRandomValues 生成一个范围内的随机数

[英]Javascript: Generate a random number within a range using crypto.getRandomValues

I understand you can generate a random number in JavaScript within a range using this function:我知道您可以使用此函数在 JavaScript 中生成一个范围内的随机数:

function getRandomInt (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

Courtesy of Ionuț G. Stan here .此处Ionuț G. Stan提供。

What I want to know is if you can generate a better random number in a range using crypto.getRandomValues() instead of Math.random().我想知道的是,您是否可以使用crypto.getRandomValues()而不是 Math.random() 在某个范围内生成更好的随机数。 I would like to be able to generate a number between 0 and 10 inclusive, or 0 - 1, or even 10 - 5000 inclusive.我希望能够生成 0 到 10 之间的数字,或 0 - 1,甚至 10 - 5000 包括在内。

You'll note Math.random() produces a number like: 0.8565239671015732 .你会注意到 Math.random() 产生一个类似的数字: 0.8565239671015732

The getRandomValues API might return something like: getRandomValues API 可能返回如下内容:

  • 231 with Uint8Array(1)第231 Uint8Array(1)
  • 54328 with Uint16Array(1) 54328Uint16Array(1)
  • 355282741 with Uint32Array(1) . 355282741Uint32Array(1)

So how to translate that back to a decimal number so I can keep with the same range algorithm above?那么如何将其转换回十进制数,以便我可以使用上述相同的范围算法? Or do I need a new algorithm?或者我需要一个新的算法?

Here's the code I tried but it doesn't work too well.这是我尝试过的代码,但效果不佳。

function getRandomInt(min, max) {       
    // Create byte array and fill with 1 random number
    var byteArray = new Uint8Array(1);
    window.crypto.getRandomValues(byteArray);

    // Convert to decimal
    var randomNum = '0.' + byteArray[0].toString();

    // Get number in range
    randomNum = Math.floor(randomNum * (max - min + 1)) + min;

    return randomNum;
}

At the low end (range 0 - 1) it returns more 0's than 1's.在低端(范围 0 - 1),它返回的 0 多于 1。 What's the best way to do it with getRandomValues()?使用 getRandomValues() 做到这一点的最佳方法是什么?

Many thanks非常感谢

The easiest way is probably by rejection sampling (see http://en.wikipedia.org/wiki/Rejection_sampling ).最简单的方法可能是拒绝抽样(参见http://en.wikipedia.org/wiki/Rejection_sampling )。 For example, assuming that max - min is less than 256:例如,假设max - min小于 256:

function getRandomInt(min, max) {       
    // Create byte array and fill with 1 random number
    var byteArray = new Uint8Array(1);
    window.crypto.getRandomValues(byteArray);

    var range = max - min + 1;
    var max_range = 256;
    if (byteArray[0] >= Math.floor(max_range / range) * range)
        return getRandomInt(min, max);
    return min + (byteArray[0] % range);
}

IMHO, the easiest way to generate a random number in a [min..max] range with window.crypto.getRandomValues() is described here .恕我直言, 这里描述使用window.crypto.getRandomValues()[min..max]范围内生成随机数的最简单方法。

An ECMAScript 2015-syntax code, in case the link is TL;TR : ECMAScript 2015 语法代码,以防链接为TL;TR

function getRandomIntInclusive(min, max) {
    const randomBuffer = new Uint32Array(1);

    window.crypto.getRandomValues(randomBuffer);

    let randomNumber = randomBuffer[0] / (0xffffffff + 1);

    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(randomNumber * (max - min + 1)) + min;
}

Necromancing.死灵法术。
Well, this is easy to solve.嗯,这很容易解决。

Consider random number in ranges without crypto-random :考虑没有 crypto-random 范围内的随机数

// Returns a random number between min (inclusive) and max (exclusive)
function getRandomArbitrary(min, max) {
    return Math.random() * (max - min) + min;
}

/**
 * Returns a random integer between min (inclusive) and max (inclusive).
 * The value is no lower than min (or the next integer greater than min
 * if min isn't an integer) and no greater than max (or the next integer
 * lower than max if max isn't an integer).
 * Using Math.round() will give you a non-uniform distribution!
 */
function getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

So all you need to do is replace Math.random with a random from crypt.所以你需要做的就是用来自 crypt 的 random 替换 Math.random 。

So what does Math.random do ?那么 Math.random 有什么作用呢?
According to MDN , the Math.random() function returns a floating-point, pseudo-random number in the range 0 to less than 1 (inclusive of 0, but not 1)根据MDN , Math.random() 函数返回一个浮点伪随机数,范围为 0 到小于 1(包括 0,但不包括 1)

So we need a crypto-random number >= 0 and < 1 ( not <= ).所以我们需要一个加密随机数 >= 0 和< 1 (不是 <= )。

So, we need a non-negative (aka. UNSIGNED) integer from getRandomValues.所以,我们需要一个来自 getRandomValues 的非负(也就是 UNSIGNED)整数。
How do we do this?我们如何做到这一点?

Simple: Instead of getting an integer, and then doing Math.abs, we just get an UInt:简单:而不是获取一个整数,然后执行 Math.abs,我们只得到一个 UInt:

var randomBuffer = new Int8Array(4); // Int8Array = byte, 1 int = 4 byte = 32 bit 
window.crypto.getRandomValues(randomBuffer);
var dataView = new DataView(array.buffer);
var uint = dataView.getUint32();

The shorthand version of which is其简写版本是

var randomBuffer = new Uint32Array(1);
(window.crypto || window.msCrypto).getRandomValues(randomBuffer);
var uint = randomBuffer[0];

Now all we need to do is divide uint by uint32.MaxValue (aka 0xFFFFFFFF) to get a floating-point number.现在我们需要做的就是将 uint 除以 uint32.MaxValue(又名 0xFFFFFFFF)来得到一个浮点数。 And because we cannot have 1 in the result-set, we need to divide by (uint32.MaxValue+1) to ensure the result is < 1.并且因为结果集中不能有 1,所以我们需要除以 (uint32.MaxValue+1) 以确保结果 < 1。
Dividing by (UInt32.MaxValue + 1) works because a JavaScript integer is a 64-bit floating-point number internally, so it is not limited at 32 bit.除以 (UInt32.MaxValue + 1) 是有效的,因为 JavaScript 整数在内部是 64 位浮点数,因此它不限于 32 位。

function cryptoRand()
{
    var array = new Int8Array(4);
    (window.crypto || window.msCrypto).getRandomValues(array);
    var dataView = new DataView(array.buffer);

    var uint = dataView.getUint32();
    var f = uint / (0xffffffff + 1); // 0xFFFFFFFF = uint32.MaxValue (+1 because Math.random is inclusive of 0, but not 1) 

    return f;
}

the shorthand of which is其简写是

function cryptoRand()
{
    const randomBuffer = new Uint32Array(1);
    (window.crypto || window.msCrypto).getRandomValues(randomBuffer);
    return ( randomBuffer[0] / (0xffffffff + 1) );
}

Now all you need to do is replace Math.random() with cryptoRand() in the above functions.现在您需要做的就是将上述函数中的 Math.random() 替换为 cryptoRand()。

Note that if crypto.getRandomValues uses the Windows-CryptoAPI on Windows to get the random bytes, you should not consider these values a truly cryptographically secure source of entropy.请注意,如果 crypto.getRandomValues 在 Windows 上使用 Windows-CryptoAPI 来获取随机字节,则不应将这些值视为真正加密安全的熵源。

Rando.js uses crypto.getRandomValues to basically do this for you Rando.js使用crypto.getRandomValues基本上为你做这件事

 console.log(rando(5, 10));
 <script src="https://randojs.com/2.0.0.js"></script>

This is carved out of the source code if you want to look behind the curtain:如果你想看看幕后,这是从源代码中雕刻出来的:

 var cryptoRandom = () => { try { var cryptoRandoms, cryptoRandomSlices = [], cryptoRandom; while ((cryptoRandom = "." + cryptoRandomSlices.join("")).length < 30) { cryptoRandoms = (window.crypto || window.msCrypto).getRandomValues(new Uint32Array(5)); for (var i = 0; i < cryptoRandoms.length; i++) { var cryptoRandomSlice = cryptoRandoms[i].toString().slice(1, -1); if (cryptoRandomSlice.length > 0) cryptoRandomSlices[cryptoRandomSlices.length] = cryptoRandomSlice; } } return Number(cryptoRandom); } catch (e) { return Math.random(); } }; var min = 5; var max = 10; if (min > max) var temp = max, max = min, min = temp; min = Math.floor(min), max = Math.floor(max); console.log( Math.floor(cryptoRandom() * (max - min + 1) + min) );

Many of these answers are going to produce biased results.这些答案中的许多都会产生有偏见的结果。 Here's an unbiased solution.这是一个公正的解决方案。

function random(min, max) {
    const range = max - min + 1
    const bytes_needed = Math.ceil(Math.log2(range) / 8)
    const cutoff = Math.floor((256 ** bytes_needed) / range) * range
    const bytes = new Uint8Array(bytes_needed)
    let value
    do {
        crypto.getRandomValues(bytes)
        value = bytes.reduce((acc, x, n) => acc + x * 256 ** n, 0)
    } while (value >= cutoff)
    return min + value % range
}

If you are using Node.js, it is safer to use the cryptographically secure pseudorandom crypto.randomInt.如果您使用的是 Node.js,则使用加密安全的伪随机 crypto.randomInt 会更安全。 Don't go write this kind of sensitive methods if you don't know what you are doing and without peer review.如果您不知道自己在做什么并且没有同行评审,请不要编写这种敏感的方法。

Official documentation 官方文档

crypto.randomInt([min, ]max[, callback]) crypto.randomInt([min, ]max[, callback])

Added in: v14.10.0, v12.19.0加入: v14.10.0, v12.19.0

  • min <integer> Start of random range (inclusive). min <integer>随机范围的开始(包括)。 Default: 0.默认值:0。
  • max <integer> End of random range (exclusive). max <integer>随机范围的结束(不包括)。
  • callback <Function> function(err, n) {} . callback <Function> function(err, n) {}

Return a random integer n such that min <= n < max.返回一个随机整数 n,使得 min <= n < max。 This implementation avoids modulo bias .这种实现避免了模偏差

The range ( max - min ) must be less than 2^48.范围 ( max - min ) 必须小于 2^48。 min and max must be safe integers . minmax必须是安全整数

If the callback function is not provided, the random integer is generated synchronously.如果未提供回调函数,则同步生成随机整数。

// Asynchronous
crypto.randomInt(3, (err, n) => {
  if (err) throw err;
  console.log(`Random number chosen from (0, 1, 2): ${n}`);
});
// Synchronous
const n = crypto.randomInt(3);
console.log(`Random number chosen from (0, 1, 2): ${n}`);
// With `min` argument
const n = crypto.randomInt(1, 7);
console.log(`The dice rolled: ${n}`);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用crypto.getRandomValues中的随机数对数组进行随机化? - How to randomize an array with random numbers from crypto.getRandomValues? crypto.getRandomValues是否会返回负值? - Will crypto.getRandomValues return negative values? 如何用 crypto.getRandomValues() 替换 Math.random() 并保持相同的结果? - How to replace Math.random() with crypto.getRandomValues() and keep same result? IE9和Firefox的Crypto.getRandomValues实现 - Crypto.getRandomValues Implementation for IE9 and Firefox Crypto.getRandomValues的熵源是什么? - What is the source of entropy for Crypto.getRandomValues? Inte.net Explorer 11 中的 crypto.getRandomValues 有什么问题? - What is wrong with crypto.getRandomValues in Internet Explorer 11? Javascript:使用范围内的范围生成随机数 - Javascript: Generate random number using a range inside a range crypto.getRandomValues(new Uint32Array(1))[0] / lim 可以为负数吗? - Can crypto.getRandomValues(new Uint32Array(1))[0] / lim ever be negative? 如何使用window.crypto.getRandomValues获取特定范围内的随机值 - How to use window.crypto.getRandomValues to get random values in a specific range 一定范围内的随机数,但不包括Javascript中的一些 - Random number within a range but exclude some in Javascript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM