所以我对老好人很熟悉
Math.floor(Math.random() * (max - min + 1)) + min;
这对小数字非常有效,但是当数字变大时,它很快就会变得有偏差,并且只返回比它低一个零的数字(例如,0
和 1e100 之间的随机数
几乎总是(每次我测试时,自从我使用 for 循环生成大量数字以来已经有数十亿次)返回 [x]e99
)。是的,我等待程序生成那么多数字的时间很长,两次。至此,可以安全地假设对于所有实际用途,输出始终为 [x]e99
。
所以接下来我尝试了这个
Math.floor(Math.pow(max - min + 1, Math.random())) + min;
虽然这对于大范围来说非常有效,但对于小范围来说就不行了。所以我的问题是如何做到这两点 - 能够生成小随机数和大随机数而没有任何偏差(或最小偏差到不明显的程度)?
注意:我使用的是 Decimal.js处理 -1e2043
< x <1e2043
范围内的数字,但由于它是相同的算法,我在上面显示了普通的 JavaScript 形式以防止混淆。我可以采用普通答案并将其转换为 Decimal.js没有任何问题,所以请随意回答。
注释 #2:我想平均分配获得大量数字的几率。例如,1e33
应该与我的 0-1e100
示例中的 1e90
具有相同的赔率。但与此同时,我需要支持更小的数字和范围。
最佳答案
您的问题是精度。这就是您首先使用 Decimal.js 的原因。与 JS 中的所有其他 Number 一样,Math.random()
仅支持 53 位精度(某些浏览器甚至只用于创建高 32 位随机数)。但是您的值 1e100
需要 333 位精度。因此,较低的 280 位 (100 位中的小数点后约 75 位) 在您的公式中被丢弃。
但 Decimal.js 提供了一个 random()
方法。为什么不用那个?
function random(min, max){
var delta = new Decimal(max).sub(min);
return Decimal.random( +delta.log(10) ).mul(delta).add(min);
}
使用 e+99
得到这么多值的另一个“问题”是概率。对于 0 .. 1e100
范围,获得某些指数的概率是
e+99 => 90%,
e+98 => 9%,
e+97 => 0.9%,
e+96 => 0.09%,
e+95 => 0.009%,
e+94 => 0.0009%,
e+93 => 0.00009%,
e+92 => 0.000009%,
e+91 => 0.0000009%,
e+90 => 0.00000009%,
and so on
因此,如果您生成 100 亿个数字,从统计上讲,您将获得一个高达 1e+90
的值。这就是赔率。
I want to even out those odds for large numbers. 1e33 should have the same odds as 1e90 for example
好的,那么让我们在 min ... max
范围内生成 10 个随机数。
function random2(min, max){
var a = +Decimal.log10(min),
b = +Decimal.log10(max);
//trying to deal with zero-values.
if(a === -Infinity && b === -Infinity) return 0; //a random value between 0 and 0 ;)
if(a === -Infinity) a = Math.min(0, b-53);
if(b === -Infinity) b = Math.min(0, a-53);
return Decimal.pow(10, Decimal.random(Math.abs(b-a)).mul(b-a).add(a) );
}
现在指数几乎均匀分布,但值有点倾斜。因为 101 到 101.5 10 .. 33
与 101.5 到 10 的概率相同2 34 .. 100
关于javascript - 在 Node.js 中的包含范围内生成大随机数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45580596/