I was preparing to start a project that will use JavaScript. To get ready, I coded up a few short demos of my favorite problems, using JavaScript. One of these favorite problems is Parrando’s Paradox. It’s one of the most fascinating problems I’ve ever seen.
Briefly, you have two completely different games that lose slowly but surely. However, by playing the two games in a certain sequence, you will actually win over time. Remarkable.
The first game is to flip a biased coin. You win $1 if the result is Heads, but lose $1 if the result is Tails. The coin is biased slightly so that the probability of Heads is 0.495 and the probability of Tails is 0.505 — clearly you will get Tails more often and so you will lose steadily over time.
In the second game, you take turns spinning one of two spinners, call them spinner A and spinner B. Spinner A is very bad and you have a probability of winning of only 0.095. Spinner B is very good and you have a probability of winning of 0.745. To play the game, you start by checking how much money you currently have. If the current amount of money you have is an even multiple of 3 ($3, $6, $9, etc.), you play spinner A. If your current money is not a multiple of 3, you play spinner B.
Although it’s not entirely obvious, if you play the spinner game, you will slowly but surely lose money. About one-third of the time you’ll play spinner A and win only 0.095 of the time, and about two-thirds of the time you’ll play spinner B and win 0.745 of the time. The approximate expectation per play is (1/3)(0.095)(+1) + (1/3)(0.905)(-1) + (2/3)(0.745)(+1) + (2/3)(0.255)(-1). If you do the math, it turns out your expectation is about -0.007 per play.
Let C be the coin flip game and let S be the pick then spin a spinner game. Now, suppose you take turns, playing C then S then C then S and so on. You will steadily lose over time as expected — if you play two losing games you will lose over time as expected.
However, if you alternate by playing in the order C, C, S, S, C, C, S, S, and so on, incredibly, you will slowly win over time! Two losing games combine to give a winning game — absolutely astonishing.
It isn’t really a paradox once you understand the tricky math involved. The Wikipedia article on Parrando’s Paradox explains what’s happening.
I implemented a simulation using JavaScript. By playing the coin flip game C 10,000 times, the net loss was $188. By playing the spinner game S 10,000 times, the net loss was $158. But by playing CCSSCCSS . . . 10,000 times, the net gain is $680. Almost beyond belief.
Paradoxes. Left: Classic chicken and egg paradox. Center: Two families brawling at Disneyland, “The Happiest Place on Earth”. Right: A fire truck, in two senses of the word.
Code below (long).
// parrando.js // demonstrate Parrando's Paradox using JavaScript // replace "lt", "lt", "lte", "gte" with correct symbols class Erratic // sort-of random, seedable { constructor(seed) { this.seed = seed + 0.5; // avoid 0 } next() { let x = Math.sin(this.seed) * 1000; let result = x - Math.floor(x); // [0.0,1.0) this.seed = result; // for next call return result; } nextInt(lo, hi) { let x = this.next(); return Math.trunc((hi - lo) * x + lo); } } function playCoin(startCash, nTimes, rnd) { let cash = startCash; for (let i = 0; i "lt" nTimes; ++i) { let p = rnd.next(); if (p "lt" 0.495) { cash += 1; } else { cash -= 1; } } return cash; } function playSpinner(startCash, nTimes, rnd) { let cash = startCash; for (let i = 0; i "lt" nTimes; ++i) { let p = rnd.next(); if (cash % 3 == 0) { // use bad spinner if (p "lt" 0.095) { cash += 1; } else { cash -= 1; } } else { // use good spinner if (p "lt" 0.745) { cash += 1; } else { cash -=1; } } } return cash; } function playCoinCoinSpinSpin(startCash, nTimes, rnd) { let cash = startCash; for (let i = 0; i "lt" nTimes; ++i) { // flip coin let p = rnd.next(); if (p "lt" 0.495) { cash += 1; } else { cash -= 1; } // flip coin again p = rnd.next(); if (p "lt" 0.495) { cash += 1; } else { cash -= 1; } // spinner game p = rnd.next(); if (cash % 3 == 0) { // use bad spinner if (p "lt" 0.095) { cash += 1; } else { cash -= 1; } } else { // use good spinner if (p "lt" 0.745) { cash += 1; } else { cash -=1; } } // spinner game again p = rnd.next(); if (cash % 3 == 0) { // use bad spinner if (p "lt" 0.095) { cash += 1; } else { cash -= 1; } } else { // use good spinner if (p "lt" 0.745) { cash += 1; } else { cash -=1; } } } return cash; } function main() { process.stdout.write("\033[0m"); // reset process.stdout.write("\x1b[1m" + "\x1b[37m"); // bright white console.log("Begin Parrando's Paradox demo (w/ JavaScript) \n"); let rnd = new Erratic(2); // lightweight random numbers let coinCash = playCoin(0, 10000, rnd); console.log("\nAfter 10,000 flips cash is "); console.log(coinCash.toString()); let spinCash = playSpinner(0, 10000, rnd); console.log("\nAfter 10,000 spins cash is "); consolelog(spinCash.toString()); let ccssCash = playCoinCoinSpinSpin(0, 10000, rnd); console.log("\nAfter 10,000 Coin Coin Spin Spin, cash is "); console.log(ccssCash.toString()); console.log("\nEnd demo \n"); } main();
You must be logged in to post a comment.