Unveiling the Monty Hall Problem: A JavaScript Simulation
Vardan Hakobyan
Posted on September 21, 2024
Have you ever pondered the Monty Hall problem? This classic probability puzzle has baffled both mathematicians and casual thinkers for years. Today, we’re going to demystify this counterintuitive puzzle using the power of JavaScript.
The Monty Hall Problem: A Quick Recap
Imagine this scenario:
- You’re on a game show facing three closed doors.
- Behind one door lies a shiny new car; behind the other two, just goats.
- You pick a door (let’s call it Door A), hoping for the car.
- The host, Monty Hall, who knows what’s behind each door, opens another door revealing a goat.
- Now, Monty offers you a choice: stick with Door A or switch to the remaining unopened door.
Here’s the million-dollar question: Should you switch doors or stay put? Does it even matter?
Surprisingly, the odds aren’t 50–50. If you switch, you’ll win the car 2/3 of the time! Sounds counterintuitive, right? Let’s prove it with code.
Setting Up Our JavaScript Simulator
First, let’s create a simple Node.js app. If you’re familiar with the process, feel free to skip this section.
Otherwise, follow these steps:
- Open your terminal and navigate to your desired project folder.
- Run these commands:
mkdir monty-hall-problem
cd monty-hall-problem
Now, create an entry file, where we will write the actual code:
touch index.js
That’s it! Now, open this file let’s start the actual coding!
Coding the Monty Hall Simulation
Let’s break down our simulation into manageable steps:
- Model three doors
- Randomly place the car behind one door
- Simulate the player’s initial choice
- Simulate Monty opening a door with a goat
- Simulate choosing the remaining door when the player decides to switch
- Calculate wins for both strategies: switching and staying
Let’s introduce a constant for the doors count so we won’t have the magic number 3
in our code.
const DOORS_COUNT = 3;
We’ll keep the win numbers in respective variables:
let winsWithoutDoorChange = 0;
let winsWithDoorChange = 0;
Let’s create a function that randomly generates an integer between 0
and given maxNumber
:
const generateRandomNumber = (maxNumber) => {
return Math.floor(Math.random() * maxNumber);
}
We have 3 doors, starting from index with 0
, so maxNumber
will be 2
. Let’s randomly select our door with car and the one the player chose:
const doorWithCar = generateRandomNumber(DOORS_COUNT);
const chosenDoor = generateRandomNumber(DOORS_COUNT);
Then, we need to choose some door that the host will open. It must not be the one the player chose, and also must not be one with car. We can do that with this code (one of rare cases when do/while is actually appropriate):
do {
openDoorWithGoat = generateRandomNumber(DOORS_COUNT);
} while ([chosenDoor, doorWithCar].includes(openDoorWithGoat));
Ok, now we have one door chosen by player and another open door with a goat. The player can change their decision and choose the last door. Let’s simulate it:
const newChosenDoor = [0, 1, 2]
.find(door => ![chosenDoor, openDoorWithGoat].includes(door));
Here we iterate over an array of 3 elements (doors), and select the only one remaining (the door that is not selected and is not open). This is the new door that the player chooses.
All that remains is to check whether the player has won and increase the corresponding counter:
if (chosenDoor === doorWithCar) {
winsWithoutDoorChange++;
}
if (newChosenDoor === doorWithCar) {
winsWithDoorChange++;
}
That’s it! Let’s put this logic in a for
loop and try iterating with different numbers. Remember, the more iterations, the more accurate the final result will be.
Here’s the complete code for our simulation:
const ATTEMPTS_COUNT = 1000_000;
const DOORS_COUNT = 3;
const generateRandomNumber = (maxNumber) => {
return Math.floor(Math.random() * maxNumber);
}
const simulateMontyHall = (attempts) => {
let winsWithoutDoorChange = 0;
let winsWithDoorChange = 0;
for (let i = 0; i < attempts; i++) {
const doorWithCar = generateRandomNumber(DOORS_COUNT);
const chosenDoor = generateRandomNumber(DOORS_COUNT);
let openDoorWithGoat;
do {
openDoorWithGoat = generateRandomNumber(DOORS_COUNT);
} while ([chosenDoor, doorWithCar].includes(openDoorWithGoat));
const newChosenDoor = [0, 1, 2]
.find(door => ![chosenDoor, openDoorWithGoat].includes(door));
if (chosenDoor === doorWithCar) {
winsWithoutDoorChange++;
}
if (newChosenDoor === doorWithCar) {
winsWithDoorChange++;
}
}
return {
winsWithoutDoorChange,
winsWithDoorChange,
}
}
const {
winsWithoutDoorChange,
winsWithDoorChange,
} = simulateMontyHall(ATTEMPTS_COUNT);
const withoutChangeWinProbability = (winsWithoutDoorChange / ATTEMPTS_COUNT * 100).toFixed(2);
const withChangeWinProbability = (winsWithDoorChange / ATTEMPTS_COUNT * 100).toFixed(2);
console.log(
`Without change, won ${winsWithoutDoorChange} out of ${ATTEMPTS_COUNT} attempts. Probability is ${withoutChangeWinProbability}%`,
);
console.log(
`With change, won ${winsWithDoorChange} out of ${ATTEMPTS_COUNT} attempts. Probability is ${withChangeWinProbability}%`,
);
To start the program, simply run node index.js
in terminal. You will have ~33.3% of wins if the player doesn’t change their initial choice, and ~66.6% of wins in case of change.
If you don’t want to create and run the program locally, here is a link to Codepen. Run it and check the results.
Hope you enjoyed it! If you have any notes, remarks or questions, please share them in the comments.
Prefer watching instead? Check out the video on YouTube.
Stay updated with the latest JavaScript and software development news! Join my Telegram channel for more insights and discussions: TechSavvy: Frontend & Backend.
Posted on September 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.