Javascript:使用crypto.getRandomValues生成一个范围内的随机数
Posted
技术标签:
【中文标题】Javascript:使用crypto.getRandomValues生成一个范围内的随机数【英文标题】:Javascript: Generate a random number within a range using crypto.getRandomValues 【发布时间】:2013-08-16 07:35:15 【问题描述】:我知道您可以使用此函数在 javascript 中生成一个范围内的随机数:
function getRandomInt (min, max)
return Math.floor(Math.random() * (max - min + 1)) + min;
感谢Ionuț G. Stan here。
我想知道的是,您是否可以使用crypto.getRandomValues() 而不是 Math.random() 在一个范围内生成一个更好的随机数。我希望能够生成一个介于 0 和 10(含)之间的数字,或者 0-1,甚至 10-5000(含)的数字。
您会注意到 Math.random() 生成的数字如下:0.8565239671015732。
getRandomValues API 可能返回如下内容:
231 与Uint8Array(1)
54328 与Uint16Array(1)
355282741 与Uint32Array(1)
。
那么如何将其转换回十进制数,以便我可以使用上述相同的范围算法?还是我需要一种新算法?
这是我尝试过的代码,但效果不太好。
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;
在低端(范围 0 - 1),它返回的 0 多于 1。使用 getRandomValues() 的最佳方法是什么?
非常感谢
【问题讨论】:
【参考方案1】:最简单的方法可能是拒绝抽样(参见http://en.wikipedia.org/wiki/Rejection_sampling)。例如,假设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);
【讨论】:
在github.com/EFForg/OpenWireless/pull/195 的讨论中设计了一个更通用的(int > 256)解决方案。我冒昧地用这个建议的修订版编辑了 OP。 :) 具体见Diceware.prototype.random
in github.com/EFForg/OpenWireless/blob/master/app/js/diceware.js。【参考方案2】:
恕我直言,使用window.crypto.getRandomValues()
在[min..max]
范围内生成随机数的最简单方法描述为here。
一个 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;
【讨论】:
0xFFFFFFFF = uint32.MaxValue(+1 因为 Math.random 包含 0,但不包含 1)【参考方案3】:其中许多答案都会产生有偏见的结果。这是一个公正的解决方案。
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
【讨论】:
【参考方案4】:死灵术。 嗯,这很容易解决。
考虑random number in ranges without 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;
所以您需要做的就是将 Math.random 替换为来自 crypt 的随机数。
那么 Math.random 做了什么? 根据MDN,Math.random() 函数返回一个浮点伪随机数,范围为 0 到小于 1(包括 0,但不包括 1)
所以我们需要一个加密随机数 >= 0 和 1(不是 )。
因此,我们需要一个来自 getRandomValues 的 非负(又名 UNSIGNED)整数。 我们如何做到这一点?
简单: 我们没有得到一个整数,然后执行 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();
它的简写版本是
var randomBuffer = new Uint32Array(1);
(window.crypto || window.msCrypto).getRandomValues(randomBuffer);
var uint = randomBuffer[0];
现在我们需要做的就是将 uint 除以 uint32.MaxValue(又名 0xFFFFFFFF)得到一个浮点数。而且因为我们不能在结果集中有 1,所以我们需要除以 (uint32.MaxValue+1) 以确保结果为 除以 (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;
它的简写是
function cryptoRand()
const randomBuffer = new Uint32Array(1);
(window.crypto || window.msCrypto).getRandomValues(randomBuffer);
return ( randomBuffer[0] / (0xffffffff + 1) );
现在您需要做的就是将上述函数中的 Math.random() 替换为 cryptoRand()。
请注意,如果 crypto.getRandomValues 使用 Windows 上的 Windows-CryptoAPI 来获取随机字节,you should not consider these values a truly cryptographically secure source of entropy.
【讨论】:
【参考方案5】:Rando.js 使用crypto.getRandomValues
基本上为你做这件事
console.log(rando(5, 10));
<script src="https://randojs.com/2.0.0.js"></script>
如果你想看看幕后,这是从源代码中雕刻出来的:
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) );
【讨论】:
【参考方案6】:如果您使用的是 Node.js,则使用加密安全的伪随机 crypto.randomInt 会更安全。如果您不知道自己在做什么并且没有同行评审,请不要编写这种敏感的方法。
Official documentation
crypto.randomInt([min, ]max[, callback])
添加于:v14.10.0、v12.19.0
min
<integer>
随机范围的开始(包括)。默认值:0。
max
<integer>
随机范围结束(不包括)。
callback
<Function>
function(err, n)
.
返回一个随机整数 n,使得 min modulo bias。
范围 (max - min
) 必须小于 2^48。 min
和 max
必须是 safe integers。
如果没有提供回调函数,则同步生成随机整数。
// 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`);
【讨论】:
以上是关于Javascript:使用crypto.getRandomValues生成一个范围内的随机数的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript 在HTML中使用 JavaScript
使用 javascript 确定 javascript 中的堆栈深度
仅使用javascript检查由javascript创建的元素是不是存在[重复]
为啥使用 `javascript:void(0)` 而不是 `javascript:` 作为 href 不做占位符? [复制]