For a given min and max, the formula describes how many bits you'll use on average if you request u
bits at once and retry if returning the result would introduce bias.
Fortunately, the optimal strategy is simply requesting ceil(log2(max - min + 1))
bits at once. We can only get full bytes with crypto.getRandomValues
anyways, so if we have one call of crypto.getRandomValues
per function call, the best we can do is:
// Generate a random integer r with equal chance in min <= r < max.
function randrange(min, max) {
var range = max - min;
if (range <= 0) {
throw new Exception('max must be larger than min');
}
var requestBytes = Math.ceil(Math.log2(range) / 8);
if (!requestBytes) { // No randomness required
return min;
}
var maxNum = Math.pow(256, requestBytes);
var ar = new Uint8Array(requestBytes);
while (true) {
window.crypto.getRandomValues(ar);
var val = 0;
for (var i = 0;i < requestBytes;i++) {
val = (val << 8) + ar[i];
}
if (val < maxNum - maxNum % range) {
return min + (val % range);
}
}
}
If you generate many values, you may consider some optimizations, namely requesting more bytes (i.e. a larger array) in advance. If your range becomes smaller (say you want to flip a coin), than it may also be beneficial to work in a bit-based manner, i.e. request many bits upfront and then only use up the random bits you really need.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…