作者:阵风阿斯顿 | 来源:互联网 | 2023-05-22 13:47
要求和背景
我想要一个通用randomInt
函数,该函数可以处理直至(包括)Number.MIN_SAFE_INTEGER
to 的值范围,并且Number.MAX_SAFE_INTEGER
返回的值是均匀分布的。
因此,我从MDN开始,然后浏览了该Math.random
页面。他们举了一个例子,它看起来是均匀分布的。
// Returns a random integer between min (included) and max (excluded)
// Using Math.round() will give you a non-uniform distribution!
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
但是它带有以下注释。
请注意,由于Javascript中的数字是IEEE 754浮点数,具有从近似到最近的行为,因此以下函数所声明的范围(不包括Math.random()本身的范围)不准确。如果选择了很大的界限(2 ^ 53或更高),则在极少数情况下可以计算通常不包括的上限。
我想使用-(2 ^ 53-1)和2 ^ 53-1范围,因此我认为此注释不适用。然后,我注意到max - min
:对于我指定的较大范围,这将是一个问题:
示例-最大范围
Number.MAX_SAFE_INTEGER - Number.MIN_SAFE_INTEGER > Number.MAX_SAFE_INTEGER
解决方案1-不是解决方案
根据MDN示例和我的要求,我开始做一些工作,并提出以下代码。
// Returns a random integer between min (included) and max (excluded)
// Using Math.round() will give you a non-uniform distribution!
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
但是正如您所看到的,这将在我需要的较大范围之前引发错误。
解决方案2-解决了数学问题,但似乎破坏了一致性
因此,我摆弄一个小提琴,并提出以下建议。
Number.MAX_SAFE_INTEGER - Number.MIN_SAFE_INTEGER > Number.MAX_SAFE_INTEGER
虽然我们不再抛出错误并且数学似乎在最大安全整数值之内,但是我不确定这如何影响原始MDN示例的均匀分布(如果它是均匀分布的)?
我的测试似乎表明这破坏了均匀分布。
分布图
Number.MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
Number.MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER || -Number.MAX_SAFE_INTEGER;
Number.toInteger = Number.toInteger || function (inputArg) {
var number = +inputArg,
val = 0;
if (number === number) {
if (!number || number === Infinity || number === -Infinity) {
val = number;
} else {
val = (number > 0 || -1) * Math.floor(Math.abs(number));
}
}
return val;
};
function clampSafeInt(number) {
return Math.min(Math.max(Number.toInteger(number), Number.MIN_SAFE_INTEGER), Number.MAX_SAFE_INTEGER);
}
// Returns a random integer between min (included) and max (included)
// Using Math.round() will give you a non-uniform distribution!
function randomInt(min, max) {
var tmp,
val;
if (arguments.length === 1) {
max = min;
min = 0;
}
min = clampSafeInt(min);
max = clampSafeInt(max);
if (min > max) {
tmp = min;
min = max;
max = tmp;
}
tmp = max - min + 1;
if (tmp > Number.MAX_SAFE_INTEGER) {
throw new RangeError('Difference of max and min is greater than Number.MAX_SAFE_INTEGER: ' + tmp);
} else {
val = Math.floor(Math.random() * tmp) + min;
}
return val;
}
console.log(randomInt(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER));
Number.MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
Number.MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER || -Number.MAX_SAFE_INTEGER;
Number.toInteger = Number.toInteger || function (inputArg) {
var number = +inputArg,
val = 0;
if (number === number) {
if (!number || number === Infinity || number === -Infinity) {
val = number;
} else {
val = (number > 0 || -1) * Math.floor(Math.abs(number));
}
}
return val;
};
function clampSafeInt(number) {
return Math.min(Math.max(Number.toInteger(number), Number.MIN_SAFE_INTEGER), Number.MAX_SAFE_INTEGER);
}
// Returns a random integer between min (included) and max (included)
// Using Math.round() will give you a non-uniform distribution!
function randomInt(min, max) {
var tmp,
val;
if (arguments.length === 1) {
max = min;
min = 0;
}
min = clampSafeInt(min);
max = clampSafeInt(max);
if (min > max) {
tmp = min;
min = max;
max = tmp;
}
tmp = max - min + 1;
if (tmp > Number.MAX_SAFE_INTEGER) {
if (Math.floor(Math.random() * 2)) {
val = Math.floor(Math.random() * (max - 0 + 1)) + 0;
} else {
val = Math.floor(Math.random() * (0 - min + 1)) + min;
}
} else {
val = Math.floor(Math.random() * tmp) + min;
}
return val;
}
console.log(randomInt(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER));
function getData() {
var x = {},
c = 1000000,
min = -20,
max = 20,
q,
i;
for (i = 0; i
解决方案3-不是解决方案
因此,我按一下创建Box-Muller Transform
函数,以创建我认为需要的随机正态分布范围(但我的错误是我希望均匀分布)。我做了一些阅读,然后选择rejection sampling
了从分布生成观察结果的方法。了解了如何计算范围的偏差而无需使用Math.sqrt
:
如果x的值为负,则Math.sqrt()返回NaN
这就是我想出的。
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
不确定我是否正确完成了所有操作(还没有破坏正态分布),但是在较小的整数范围内,我看到的是生成的正确整数范围。
但是,当我使用范围的最大限制时(或实际上在这些限制之前),仍然存在问题。数学仍然超出Number.MAX_SAFE_INTEGER
值。从上面输出console.log(tmp);
{mean: 0, variance: 8.112963841460666e+31, deviation: 9007199254740991}
如您所见,计算出的variance
是不安全的。由于我对分布类型感到困惑,因此可以忽略该算法。
分布图
我已经包括了它,以便您可以看到我实际上已经很接近将这项工作作为正态分布发布,尽管这并不是我真正需要的。它可以帮助正在执行这种分配的人。
Number.MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
Number.MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER || -Number.MAX_SAFE_INTEGER;
Number.toInteger = Number.toInteger || function (inputArg) {
var number = +inputArg,
val = 0;
if (number === number) {
if (!number || number === Infinity || number === -Infinity) {
val = number;
} else {
val = (number > 0 || -1) * Math.floor(Math.abs(number));
}
}
return val;
};
function clampSafeInt(number) {
return Math.min(Math.max(Number.toInteger(number), Number.MIN_SAFE_INTEGER), Number.MAX_SAFE_INTEGER);
}
var boxMullerRandom = (function () {
var phase = 0,
RAND_MAX,
array,
random,
x1, x2, w, z;
if (crypto && crypto.getRandomValues) {
RAND_MAX = Math.pow(2, 32) - 1;
array = new Uint32Array(1);
random = function () {
crypto.getRandomValues(array);
return array[0] / RAND_MAX;
};
} else {
random = Math.random;
}
return function () {
if (!phase) {
do {
x1 = 2.0 * random() - 1.0;
x2 = 2.0 * random() - 1.0;
w = x1 * x1 + x2 * x2;
} while (w >= 1.0);
w = Math.sqrt((-2.0 * Math.log(w)) / w);
z = x1 * w;
} else {
z = x2 * w;
}
phase ^= 1;
return z;
}
}());
function rejectionSample(stdev, mean, from, to) {
var retVal;
do {
retVal = (boxMullerRandom() * stdev) + mean;
} while (retVal max) {
tmp = min;
min = max;
max = tmp;
}
tmp = {};
tmp.mean = (min / 2) + (max / 2);
tmp.variance = (Math.pow(min - tmp.mean, 2) + Math.pow(max - tmp.mean, 2)) / 2;
tmp.deviation = Math.sqrt(tmp.variance);
console.log(tmp);
return Math.floor(rejectionSample(tmp.deviation, tmp.mean, min, max + 1));
}
console.log(randomInt(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER));
{mean: 0, variance: 8.112963841460666e+31, deviation: 9007199254740991}
什么是解决方案?
那么,我想念什么?有一些我忽略的简单方法吗?我必须使用大数字库作为解决方案吗?如何测试分布:我有一些图表正在绘制,这对于较小的范围很好,但是不可能较大的范围?
请让我摆脱这一烦恼。:)