如何解决使用 threadlocalrandom 模拟掷骰子不会收敛到预期值
我试图验证 Java 中的 expected value formula。
我做了一些非常简单的事情,我模拟了掷骰子,但我将每一面映射到一个特定的值。所以每个值都有 1/6 的概率。
预期值确定为:232.65
我运行了很长时间的骰子模拟,准确地说是10000000
,但数字没有收敛到232.65
而是232.74
。较小的运行也波动到值 233.97
。跑到2100000000
给了232.63
。
所以我想知道我是否做错了什么。我在这里的主要前提是,我不需要模拟 20 亿次掷骰子来最终通过公式计算出预期值。
所以我想知道我是否使用了错误的随机 API。我无法获得种子,因此我无法验证每次迭代是否会发生变化。我的代码如下:
Map<Integer,Float> map = new HashMap<>();
map.put(1,17.2f);
map.put(2,11f);
map.put(3,128f);
map.put(4,1f);
map.put(5,1200f);
map.put(6,38.7f);
double sum = 0;
int count = 0;
for(Map.Entry<Integer,Float> entry: map.entrySet()) {
sum += (entry.getValue() * (1/6f));
}
System.out.println("EV " + sum);
sum = 0;
for(int j = 0; j < 2100000000; j++) {
int dice = ThreadLocalRandom.current().nextInt(1,7);
sum += map.get(dice);
++count;
}
System.out.println(sum / count);
输出:
EV 232.65000109374523
232.63201358102154
我预计在更早的运行次数之后我会一直得到 ~232.650...
评论后更新
尝试使用 BigDecimal
但没有区别:
//double sum = 0;
BigDecimal sum = BigDecimal.valueOf(0);
int count = 0;
for(Map.Entry<Integer,Float> entry: map.entrySet()) {
sum = sum.add(BigDecimal.valueOf(entry.getValue() * (1/6f)));
}
System.out.println("EV " + sum);
sum = BigDecimal.valueOf(0);
for(int j = 0; j < 10000000; j++) {
int dice = ThreadLocalRandom.current().nextInt(1,7);
sum = sum.add(BigDecimal.valueOf(map.get(dice)));
++count;
}
System.out.println(sum.divide(BigDecimal.valueOf(count)));
输出:
EV 232.6500010937452306
232.3528356843933100044325
解决方法
这正是您所期望的,有关详细信息,请参阅维基百科上的 standard error of the mean。
就您而言,您的平均值为 232.65,标准差为 ~434.65。要了解预期方差,您可以这样做:
lower = mu - 2 * sd / sqrt(n)
upper = mu + 2 * sd / sqrt(n)
对于 n=2100000000
,这给出了大约 [232.631,232.669],与您和 m0skit0 观察到的值一致。通常,您应该期望在大约 1/20 的时间内看到超出此范围的值,即这大致是 95% CI。如果您不这样做,则 RNG 不会随机统一选择替代方案。
您遇到的“缓慢”收敛是由于 sqrt(n)
,即需要 100 倍的工作才能为您提供另一个十进制数的精度。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。