Javascript 为什么这种随机化方法会产生扭曲的结果?

Javascript 为什么这种随机化方法会产生扭曲的结果?,javascript,random,Javascript,Random,我使用了两种不同的随机化方法,其中一种方法给出的结果的方差是我所期望的,而另一种方法给出的结果是有偏差的,并且也是非常一致的 方法: function randomA() { var raw = Number((Math.random()+'').substr(2)); return raw % NUM_OF_POSSIBLES; } function randomB() { var raw = Math.round(Math.random()*10000);

我使用了两种不同的随机化方法,其中一种方法给出的结果的方差是我所期望的,而另一种方法给出的结果是有偏差的,并且也是非常一致的

方法:

function randomA() {
    var raw = Number((Math.random()+'').substr(2));
    return raw % NUM_OF_POSSIBLES;
}

function randomB() {
    var raw = Math.round(Math.random()*10000);
    return raw % NUM_OF_POSSIBLES;
}
NUM\u OF\u POSSIBLES=2
时,第一种方法(
randomA()
)会产生相当一致的零数(64%)和36%的1。而
radnomB()。
如果
NUM\u OF\u POSSIBLES=5
第一种方法再次以相当一致的方式倾斜: 0:10%,1:23%,2:22%,3:22%,4:23%,而第二种方法给出了每个结果的20%左右

您可以在此处找到包含多个测试的完整代码:


为什么第一个方法是倾斜的,为什么倾斜是一致的?

我不完全确定,但我猜这与JavaScript将数字格式化为字符串时使用的舍入模式有关。在第一种情况下,您的结果取决于最后一位数字的选择,这对舍入非常敏感。如果它偏向于偶数,这可以解释你的结果。(在
NUM\u of_POSSIBLES==5的情况下,这可能是因为最后一个数字缺少5s。)在第二个例程中,结果取决于字符串表示的中间数字,这几乎与该影响无关

在第一个例行程序中,切掉最后一两个数字可能会有更好的结果

编辑我刚刚通过实验确认,如果将第一个例程更改为切掉最后一个数字:

function randomA() {
    var raw = String(Math.random());
    raw = raw.substring(2, raw.length-1);
    return raw % NUM_OF_POSSIBLES;
}

然后,当
NUM\u OF_POSSIBLES==2
5
时,这种偏差似乎消失了。我发现,随机数以这种方式工作的原因是因为java脚本使用52+1位数字(基本格式章节下的表)。所以,在随机函数返回太大的值之后,它被舍入,例如

Math.pow(2, 54) +1 
//18014398509481984
Math.pow(2, 54)  
//18014398509481984
Math.pow(2, 54)  -1
//18014398509481984
所有返回相同的值,该值被2除(因为四舍五入)

要了解更多信息,您可以播放并查看它在biniry格式中的外观,例如:

parseInt(Math.pow(2, 54) - 2).toString(2)
//"111111111111111111111111111111111111111111111111111110"
parseInt(Math.pow(2, 54) - 3).toString(2)
//"111111111111111111111111111111111111111111111111111100"
parseInt(Math.pow(2, 54) ).toString(2)
//"1000000000000000000000000000000000000000000000000000000"
parseInt(Math.pow(2, 54) -1).toString(2)
//"1000000000000000000000000000000000000000000000000000000"

之所以会出现偏差,是因为
Math.random()
的结果并不总是具有相同的长度,因此,例如
0.123
0.1235
计入“一”堆

你可以认为如果你用尾随的零来平衡长度,它会被修正,但这也不正确,因为
0.123
可能是一个四舍五入的
0.1229999999

第一种方法的实际错误取决于不精确分数中的最低有效位(%2和%5都只受最后一位的影响),在从二进制转换为十进制表示时,该分数会出现舍入错误

分数的原始二进制形式可能是均匀分布的,但在Javascript中无法读取它


现在,如果有人能解释一个四舍五入的小数的尾随数字的分布…

有些事情让我觉得这是因为你在增加模数之前的数字的大小,但我想不出为什么。这可能与
Number()
如何格式化输出有关。不过。如果没有
*10000
,两者之间是否一致?@Askanison4我希望我能同意(或反驳!),但我自己也感到困惑,办公室里的人对此有点疯狂:D@Askanison4如果我不乘,就不能模化,但如果我只乘以100,结果仍然是一样的。Javascript实际上偏向于偶数“JavaScript中的数字是IEEE 754浮点数字,具有舍入到最近的偶数行为”是的,这与舍入有关在第一种方法中切掉最后的数字效果很好,但我不确定这是如何解释结果的一致性的——对于任何数量的
NUM\u of_POSSIBLES
@MeLight——偏倚解释了2和5的结果。我看不到其他许多可能值的偏差(例如3或7)。我怀疑因子为2或5的
NUM\u of_POSSIBLES
的值将显示
randomA
的偏差,因为JavaScript生成表示的最后一个数字时存在偏差。