简体   繁体   中英

for loop in javascript is skipping else if condition half the time

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:

  • If 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) ?
  • Separately, if (flag === false) should just be if (!flag) .
  • If 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.
  • But , 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; .
  • Because you're not declaring 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; } 

You need to assign game to a var before test for its value. Otherwise, everytime yout check the value with game() you will check a new value. So it can be false at the first check and true at the second and for this reason increment your errors. Try this:

for(i=0;i<999999;i++) {
  let gameResult = game();
  if (gameResult === true) {
    truths++
  } else if (gameResult === false) {
    alternativetruths++
  } else {
    errors++
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM