Simulating a dice throw with threadlocalrandom does not converge to the expected value

Viewed 97

I was trying to verify the expected value formula in java.
I did something very trivial, I simulated the roll of a dice, but I mapped each side to a specific value. So each value has probability of 1/6.
The expected value was determined as: 232.65
I run the dice simulation a very long number of runs, 10000000 to be precise but the number did not converge to 232.65 but was 232.74. Smaller runs also were fluxuating up to the value 233.97. Runs up to 2100000000gave 232.63.
So I am wondering if I am doing something wrong. My main premise here is that I shouldn't need to simulate 2 billion throws of a dice to eventually the expected value calculated via the formula.
So I am wondering if I am using the wrong random API. I can not get the seed so I can't verify if that changes per iteration. My code is the following:

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);

Output:

EV 232.65000109374523
232.63201358102154

I was expected after a much earlier number of runs I would consistently get something ~232.650...

Update after the comments Tried using BigDecimal but there was no difference:

//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)));

Output:

EV 232.6500010937452306
232.3528356843933100044325

1 Answers

This is exactly what you should expect, see the standard error of the mean on wikipedia for more info.

In your case, you have a mean of 232.65 and a standard deviation of ~434.65. To get an idea of the expected variance you can just do:

lower = mu - 2 * sd / sqrt(n)
upper = mu + 2 * sd / sqrt(n)

For n=2100000000 this gives approximately [232.631, 232.669], consistent with the values you and m0skit0 observed. In general, you should expect to see a value outside of this bound approximately 1/20'th of the time, i.e. this is roughly a 95% CI. If you don't then the RNG isn't choosing alternatives uniformly at random.

The "slow" convergence you're experiencing is due to the sqrt(n), i.e. it takes 100 times as much work to give you another decimal digit of precision.

Related