Node.js - 如何使用crypto.randomBytes生成特定范围内的随机数

时间:2015-11-09 12:44:59

标签: node.js math random cryptography

如何使用crypto.randomBytes生成特定范围内的随机数?

我希望能够像这样生成一个随机数:

console.log(random(55, 956)); // where 55 is minimum and 956 is maximum

我只能在随机函数中使用 crypto.randomBytes 来生成此范围的随机数。

我知道如何将生成的字节从randomBytes转换为十六进制或十进制但我无法弄清楚如何在数学上从随机字节中获取特定范围内的随机数。

6 个答案:

答案 0 :(得分:17)

要生成特定范围内的随机数,您可以使用以下等式

Math.random() * (high - low) + low

但是你想使用crypto.randomBytes而不是Math.random() 此函数返回一个随机生成的字节的缓冲区。反过来,您需要将此函数的结果从字节转换为十进制。这可以使用biguint格式包完成。要安装此软件包,只需使用以下命令:

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));

答案 1 :(得分:4)

要生成[55 .. 956]范围内的数字,首先要生成[0 .. 901]范围内的随机数,其中901 = 956 - 55.然后将55添加到刚生成的数字。

要生成[0 .. 901]范围内的数字,请选取两个随机字节并屏蔽6位。这将为您提供[0 .. 1023]范围内的10位随机数。如果该数字是< = 901那么你就完成了。如果它大于901,则丢弃它并获得两个随机字节。 尝试使用MOD,将数字输入正确的范围,这会使输出失真,使其成为非随机数。

ETA:减少丢弃生成号码的机会。

由于我们从RNG获取两个字节,因此我们得到一个范围为[0 .. 65535]的数​​字。现在65535 MOD 902是591.因此,如果我们的双字节随机数小于(65535 - 591),即小于64944,我们可以安全地使用MOD运算符,因为每个数字在[0 .. 901]现在同样可能。任何两个字节的数字> = 64944仍然必须被丢弃,因为使用它会使输出远离随机。之前,不得不拒绝一个数字的机会是(1024 - 901)/ 1024 = 12%。现在拒绝的可能性是(65535 - 64944)/ 65535 = 1%。我们不太可能不得不拒绝随机生成的数字。

running <- true
while running
  num <- two byte random
  if (num < 64944)
    result <- num MOD 902
    running <- false
  endif
endwhile
return result + 55

答案 2 :(得分:3)

感谢@Mustafamg的回答和@CodesInChaos的大力帮助,我设法解决了这个问题。我做了一些调整,并将范围增加到最大256 ^ 6-1或281,474,976,710,655。可以增加范围,但是你需要为大整数使用额外的库,因为256 ^ 7-1超出了Number.MAX_SAFE_INTEGER限制。

如果有任何人遇到同样的问题,请随时使用。

&#13;
&#13;
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;
	}
}
&#13;
&#13;
&#13;

到目前为止,它工作正常,您可以将其用作非常好的随机数生成器,但我严格不建议将此函数用于任何加密服务。如果愿意,请自担风险使用它。

欢迎所有评论,建议和评论家!

答案 3 :(得分:0)

因此,大多数其他解决方案的问题在于它们扭曲了分布(您可能希望统一)。

@rossum中的伪代码缺乏概括性。 (但他在案文中提出了正确的解决方案)

// 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;
}

关于性能方面的问题,基本上该数字在50%到100%的时间内(取决于参数)在正确的范围内。在最坏的情况下,循环执行7次以上的机会少于1%,实际上,在大多数情况下,循环执行一次或两次。

random-js库承认那里的大多数解决方案都不提供具有均匀分布的随机数,而是提供了更完整的解决方案

答案 4 :(得分:0)

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;
}

有关更多信息,请参见the full sourcethe official docs

答案 5 :(得分:-1)

$sql = "SELECT *  from mytable where id=1 limit 1";
$prev = mysqli_fetch_assoc(mysqli_query($conn, $sql));

//Get the column data in the array. Before update.

$sql = "UPDATE mytable SET name = 'Woton', age = '20' WHERE id = '1'";
$conn->query($sql);

// Update data 

$sql = "SELECT *  from mytable where id=1 limit 1";
$updated = mysqli_fetch_assoc(mysqli_query($conn, $sql));

// Again run the select command to get updated data.

$UpdatedColumns=array_diff_assoc($updated,$prev);