为什么这种随机化方法会产生偏差的结果?

时间:2014-02-04 17:00:39

标签: 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);
    return raw % NUM_OF_POSSIBLES;
}

NUM_OF_POSSIBLES = 2第一种方法(randomA())产生相当一致的零(64%)和36%的1时。虽然radnomB()几乎达到了50/50。 如果NUM_OF_POSSIBLES = 5第一种方法再次以一种非常一致的方式倾斜: 0:10%,1:23%,2:22%,3:22%,4:23%,而第二个给出每个结果约20%。

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

为什么第一种方法倾斜,为什么倾斜一致?

3 个答案:

答案 0 :(得分:3)

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

通过砍掉第一个例程中的最后一个数字,你可能会得到更好的结果。

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

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

然后,NUM_OF_POSSIBLES == 25时,偏见似乎消失了。

答案 1 :(得分:1)

我发现randomA以这种方式工作的原因是因为java脚本使用带有52 + 1位的floating poing numbers(基本格式章节下的表)。因此,在随机函数返回太大的值后,它会被舍入,例如

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"

答案 2 :(得分:1)

你得到了偏见,因为Math.random()的结果并不总是长度相同,所以例如0.1230.1235计入“ones”堆。

你可以认为如果你用尾随零来表示长度是正确的,但这也是不正确的,因为0.123可能是一个舍入的0.122999999999

第一种方法的真正误差是依赖于不精确分数的最低有效位(%2和%5仅受最后一位数影响),当从二进制转换为十进制时,它已经遭遇舍入错误

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

现在,如果有人会解释舍入小数位数的尾随数字的分布......