如何使用 crypto.randomBytes 生成特定范围内的随机数?
我希望能够生成这样的随机数:
console.log(random(55, 956)); // where 55 is minimum and 956 is maximum
我只能在random函数内使用crypto.randomBytes来生成指定范围内的随机数。
我知道如何将从randomBytes生成的字节转换为十六进制或十进制,但我无法通过数学计算从随机字节中获取指定范围内的随机数。
如何使用 crypto.randomBytes 生成特定范围内的随机数?
我希望能够生成这样的随机数:
console.log(random(55, 956)); // where 55 is minimum and 956 is maximum
我只能在random函数内使用crypto.randomBytes来生成指定范围内的随机数。
我知道如何将从randomBytes生成的字节转换为十六进制或十进制,但我无法通过数学计算从随机字节中获取指定范围内的随机数。
要在特定范围内生成随机数,可以使用以下公式
Math.random() * (high - low) + low
但您想使用crypto.randomBytes而不是Math.random(),此函数返回一个带有随机生成字节的缓冲区。然后,您需要将此函数的结果从字节转换为十进制数。可以使用biguint-format软件包完成此操作。要安装此软件包,只需使用以下命令:
npm install biguint-format --save
现在你需要将 crypto.randomBytes 的结果转换为十进制,可以按照以下方式进行:
var x= crypto.randomBytes(1);
return format(x, 'dec');
现在,您可以创建以下随机函数:
var crypto = require('crypto'),
format = require('biguint-format');
function randomC (qty) {
var x= crypto.randomBytes(qty);
return format(x, 'dec');
}
function random (low, high) {
return randomC(4)/Math.pow(2,4*8-1) * (high - low) + low;
}
console.log(random(50,1000));
感谢@Mustafamg的回答以及@CodesInChaos的大力帮助,我成功解决了这个问题。我进行了一些调整,并将范围增加到最大256^6-1或281,474,976,710,655。范围可以进一步增加,但需要使用额外的大整数库,因为256^7-1超出了Number.MAX_SAFE_INTEGER的限制。
如果有人遇到同样的问题,可以随意使用此解决方案。
var crypto = require('crypto');
/*
Generating random numbers in specific range using crypto.randomBytes from crypto library
Maximum available range is 281474976710655 or 256^6-1
Maximum number for range must be equal or less than Number.MAX_SAFE_INTEGER (usually 9007199254740991)
Usage examples:
cryptoRandomNumber(0, 350);
cryptoRandomNumber(556, 1250425);
cryptoRandomNumber(0, 281474976710655);
cryptoRandomNumber((Number.MAX_SAFE_INTEGER-281474976710655), Number.MAX_SAFE_INTEGER);
Tested and working on 64bit Windows and Unix operation systems.
*/
function cryptoRandomNumber(minimum, maximum){
var distance = maximum-minimum;
if(minimum>=maximum){
console.log('Minimum number should be less than maximum');
return false;
} else if(distance>281474976710655){
console.log('You can not get all possible random numbers if range is greater than 256^6-1');
return false;
} else if(maximum>Number.MAX_SAFE_INTEGER){
console.log('Maximum number should be safe integer limit');
return false;
} else {
var maxBytes = 6;
var maxDec = 281474976710656;
// To avoid huge mathematical operations and increase function performance for small ranges, you can uncomment following script
/*
if(distance<256){
maxBytes = 1;
maxDec = 256;
} else if(distance<65536){
maxBytes = 2;
maxDec = 65536;
} else if(distance<16777216){
maxBytes = 3;
maxDec = 16777216;
} else if(distance<4294967296){
maxBytes = 4;
maxDec = 4294967296;
} else if(distance<1099511627776){
maxBytes = 4;
maxDec = 1099511627776;
}
*/
var randbytes = parseInt(crypto.randomBytes(maxBytes).toString('hex'), 16);
var result = Math.floor(randbytes/maxDec*(maximum-minimum+1)+minimum);
if(result>maximum){
result = maximum;
}
return result;
}
}
目前为止,它的工作良好,您可以将其用作非常好的随机数生成器,但我严格不建议将此函数用于任何加密服务。如果您决定使用它,那么就要自行承担风险。
欢迎所有评论、建议和批评!
cryptoRandomNumber(-1,+1)
并检查每个结果的常见性。我期望它是 1/4 | 1/2 | 1/4,而不是您可能期望的均匀的 1/3。 - CodesInChaosmax-min+1
进行乘法运算并截断结果而不是四舍五入,从数学上讲是正确的,但确保浮点数舍入不会有时(非常罕见)返回max+1
可能有点麻烦。由于您的代码没有使用双精度浮点数的全部53位,因此这应该是不可能的,但个人而言,我宁愿只使用整数算术编写代码。 - CodesInChaos现在,crypto
包中有一个 randomInt()
函数。它是在 v14.10.0 和 v12.19.0 中添加的。
console.log(crypto.randomInt(55, 957)); // where 55 is minimum and 956 is maximum
// Largest integer we can read from a buffer.
// e.g.: Buffer.from("ff".repeat(6), "hex").readUIntBE(0, 6);
const RAND_MAX = 0xFFFF_FFFF_FFFF;
const range = max - min;
const excess = RAND_MAX % range;
const randLimit = RAND_MAX - excess;
while (true) {
const x = randomBytes(6).readUIntBE(0, 6);
// If x > (maxVal - (maxVal % range)), we will get "modulo bias"
if (x > randLimit) {
// Try again
continue;
}
const n = (x % range) + min;
return n;
}
running <- true
while running
num <- two byte random
if (num < 64944)
result <- num MOD 902
running <- false
endif
endwhile
return result + 55
// Generates a random integer in range [min, max]
function randomRange(min, max) {
const diff = max - min + 1;
// finds the minimum number of bit required to represent the diff
const numberBit = Math.ceil(Math.log2(diff));
// as we are limited to draw bytes, minimum number of bytes
const numberBytes = Math.ceil(numberBit / 4);
// as we might draw more bits than required, we look only at what we need (discard the rest)
const mask = (1 << numberBit) - 1;
let randomNumber;
do {
randomNumber = crypto.randomBytes(numberBytes).readUIntBE(0, numberBytes);
randomNumber = randomNumber & mask;
// number of bit might represent a numbers bigger than the diff, in that case try again
} while (randomNumber >= diff);
return randomNumber + min;
}