我正在使用两种不同的随机化方法,而一种方法给出的结果的方差是我所期望的,另一种方法给出的结果是偏斜的并且也是非常一致的方式。
方法:
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
为什么第一种方法倾斜,为什么倾斜一致?
答案 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 == 2
或5
时,偏见似乎消失了。
答案 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.123
和0.1235
计入“ones”堆。
你可以认为如果你用尾随零来表示长度是正确的,但这也是不正确的,因为0.123
可能是一个舍入的0.122999999999
。
第一种方法的真正误差是依赖于不精确分数的最低有效位(%2和%5仅受最后一位数影响),当从二进制转换为十进制时,它已经遭遇舍入错误
分数的原始二进制形式可能是均匀分布的,但是没有办法在Javascript中读取它。
现在,如果有人会解释舍入小数位数的尾随数字的分布......