[英]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 可能返回如下内容:
Uint8Array(1)
第231 Uint8Array(1)
Uint16Array(1)
54328与Uint16Array(1)
Uint32Array(1)
. 355282741与Uint32Array(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.如果您不知道自己在做什么并且没有同行评审,请不要编写这种敏感的方法。
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 . min
和max
必须是安全整数。
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.