I'm was just fiddling around with javascript and I wrote function using Math.random that I thought would return a coin-flip. Then I was curious so I ran it through a loop to test how often it flips true/false. I found out my loop is skipping about half of the else if conditions, and was able to verify this by catching them in the errors var. So, why is it doing this?
var truths = 0;
var alternativetruths = 0;
var errors = 0;
function game() {
var score = Math.random()*10;
return score>5;
}
for(i=0;i<999999;i++) {
if (game() === true) {
truths++
} else if (game() === false) {
alternativetruths++
} else {
errors++
}
}
console.log("truths:",truths,"alternativetruths:",alternativetruths,"errors:",errors)
truths: 500393 alternativetruths: 249580 errors: 250026
Your code calls game() twice. If the first call isn't true, then the second might or might not be true.
Just call game() once and assign the result to a variable. Don't make explicit comparisons to true and false; if statements work off boolean values, and your function already returns one of those.
for (var i = 0; i < 999999; i++) {
var result = game();
if (result)
truths++;
else
alternativetruths++;
}
Because you're calling game twice, and thus getting two different random flags.
The minimal change is to remember the number:
for(i=0;i<999999;i++) {
var flag = game();
if (flag === true) {
truths++
} else if (flag === false) {
alternativetruths++
} else {
errors++
}
}
But a couple of other notes:
flag is a boolean (and it is, it's the result of the > operator), then if (flag === true) is pointless. Just use if (flag). The result of the === operator is a boolean too, so if you don't trust them to be true or false, where do you stop? :-) if ((flag === true) === true)?if (flag === false) should just be if (!flag).flag is a boolean (and it is), then if you have if (flag), having else if (!flag) and then else doesn't make any sense. There is no way you'll reach that final else. Just if (flag) { } else { } is all you need.game isn't fair, it has a very slight bias toward returning false. Remember, the range returned by Math.random() * 10 is 0 to just under 10, so checking for > 5 means you're skipping the midpoint. Because you're dealing with very small fractional numbers, you don't notice the bias, but it's there; it would be more obvious if you rounded to whole numbers, at which point it would be roughly 40%/60% true/false. You want >= 5 for fair results.game can rather moer succinctly be written: return Math.random() >= 0.5;.i, you're falling prey to The Horror of Implicit Globals (that's a post on my anemic little blog). Remember to declare your variables.Re > vs. >=, here's an example where I've rounded to whole numbers to make the effect clearer:
for (var n = 0; n < 4; ++n) {
setTimeout(test.bind(null, n, true), 200 * n);
setTimeout(test.bind(null, n, false), 200 * n + 100);
}
function test(num, gte) {
var truths = 0;
var alternativetruths = 0;
var errors = 0;
function game() {
var score = Math.floor(Math.random() * 10);
return gte ? score >= 5 : score > 5;
}
for (var i = 0; i < 999999; i++) {
var flag = game();
if (flag) {
truths++;
} else {
alternativetruths++;
}
}
showStats(num, gte, truths, alternativetruths);
}
function showStats(num, gte, truths, alternativetruths) {
var total = truths + alternativetruths; // Should be 999999 of course
var truthsPercent = (truths * 100) / total;
var altPercent = (alternativetruths * 100) / total;
console.log(num, gte ? ">=" : ">", "truths:", truths, "alternativetruths:", alternativetruths, "(" + truthsPercent.toFixed(2) + "% vs. " + altPercent.toFixed(2) + "%)");
}
.as-console-wrapper {
max-height: 100% !important;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With