最近看韩国电视剧【D.P:逃兵追缉令】里面提到一个有趣的数学概率游戏 -> 蒙提霍尔问题
意思是:参赛者会看见三扇门,其中一扇门的里面有一辆汽车,选中里面是汽车的那扇门,就可以赢得该辆汽车,另外两扇门里面则都是一只山羊,让你任意选择其中一个,然后打开其余两个门中的一个并且是山羊(去掉一个错误答案),这时,让你重新选择。那么你是会坚持原来的选择,还是换选另外一个未被打开过的门呢?
大家可以想一想如果是自己,我们是会换还是不会换?
好了,我当时看到后感觉很有意思,所以我简单写了一套代码,源码贴在下面,大家可以验证一下,先告诉大家,换:赢得汽车的概率是2/3,不换:赢得汽车的概率是1/3
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>蒙提霍尔问题模拟器</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: "Microsoft YaHei", sans-serif;
background: #f4f6f9;
color: #333;
display: flex;
justify-content: center;
padding: 30px;
}
.container {
width: 900px;
max-width: 100%;
background: white;
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
margin-bottom: 10px;
}
.desc {
text-align: center;
color: #666;
margin-bottom: 30px;
line-height: 1.6;
}
.doors {
display: flex;
gap: 20px;
justify-content: center;
margin-bottom: 30px;
flex-wrap: wrap;
}
.door {
width: 180px;
height: 260px;
background: linear-gradient(145deg, #c98b2b, #a86b14);
border-radius: 12px;
position: relative;
cursor: pointer;
transition: all 0.25s ease;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 28px;
font-weight: bold;
user-select: none;
}
.door:hover {
transform: translateY(-5px);
}
.door.open {
background: #ddd;
color: #222;
}
.door.selected {
border: 6px solid #4caf50;
}
.door.revealed-goat {
background: #999;
}
.door.win {
background: gold;
color: #222;
}
.controls {
text-align: center;
margin-top: 20px;
}
button {
border: none;
background: #1677ff;
color: white;
padding: 12px 20px;
border-radius: 10px;
font-size: 16px;
cursor: pointer;
margin: 8px;
transition: 0.2s;
}
button:hover {
opacity: 0.9;
}
button:disabled {
background: #999;
cursor: not-allowed;
}
.message {
text-align: center;
margin-top: 20px;
font-size: 20px;
font-weight: bold;
min-height: 30px;
}
.stats {
margin-top: 30px;
background: #f8f8f8;
padding: 20px;
border-radius: 12px;
line-height: 1.8;
}
.highlight {
color: #1677ff;
font-weight: bold;
}
.footer {
margin-top: 20px;
color: #666;
font-size: 14px;
line-height: 1.7;
}
</style>
</head>
<body>
<div class="container">
<h1>🎲 蒙提霍尔问题模拟器</h1>
<div class="desc">
三扇门后面:<br>
一扇是 🚗 汽车,两扇是 🐐 山羊。<br><br>
你先选一扇门,主持人会帮你打开一扇有山羊的门。<br>
然后你可以选择:<br>
<span class="highlight">坚持原选择</span>
或
<span class="highlight">换门</span>
</div>
<div class="doors" id="doors"></div>
<div class="controls">
<button id="stayBtn" disabled>坚持不换</button>
<button id="switchBtn" disabled>换门</button>
<button onclick="resetGame()">重新开始</button>
</div>
<div class="message" id="message"></div>
<div class="stats">
<h3>📊 统计数据</h3>
<div>总局数:<span id="total">0</span></div>
<hr>
<div>
坚持不换:
胜利 <span id="stayWin">0</span> 次 /
尝试 <span id="stayTotal">0</span> 次
</div>
<div>
胜率:
<span class="highlight" id="stayRate">0%</span>
</div>
<br>
<div>
换门:
胜利 <span id="switchWin">0</span> 次 /
尝试 <span id="switchTotal">0</span> 次
</div>
<div>
胜率:
<span class="highlight" id="switchRate">0%</span>
</div>
</div>
<div class="footer">
💡 理论上:<br>
- 坚持不换胜率 ≈ 33%<br>
- 换门胜率 ≈ 66%<br><br>
多玩几次你就会发现:<br>
“换门”真的更容易赢 🚀
</div>
</div>
<script>
const doorsContainer = document.getElementById("doors");
const message = document.getElementById("message");
const stayBtn = document.getElementById("stayBtn");
const switchBtn = document.getElementById("switchBtn");
let prizeDoor;
let selectedDoor = null;
let revealedDoor = null;
let gameFinished = false;
const stats = {
total: 0,
stayWin: 0,
stayTotal: 0,
switchWin: 0,
switchTotal: 0
};
function createDoors() {
doorsContainer.innerHTML = "";
for (let i = 0; i < 3; i++) {
const door = document.createElement("div");
door.className = "door";
door.dataset.index = i;
door.innerText = "🚪";
door.onclick = () => selectDoor(i);
doorsContainer.appendChild(door);
}
}
function resetGame() {
prizeDoor = Math.floor(Math.random() * 3);
selectedDoor = null;
revealedDoor = null;
gameFinished = false;
stayBtn.disabled = true;
switchBtn.disabled = true;
message.innerText = "";
createDoors();
}
function selectDoor(index) {
if (selectedDoor !== null) return;
selectedDoor = index;
const doors = document.querySelectorAll(".door");
doors[index].classList.add("selected");
const possibleReveal = [];
for (let i = 0; i < 3; i++) {
if (i !== selectedDoor && i !== prizeDoor) {
possibleReveal.push(i);
}
}
revealedDoor =
possibleReveal[Math.floor(Math.random() * possibleReveal.length)];
doors[revealedDoor].classList.add("open", "revealed-goat");
doors[revealedDoor].innerText = "🐐";
stayBtn.disabled = false;
switchBtn.disabled = false;
message.innerText =
"主持人帮你打开了一扇山羊门!你要换门吗?";
}
stayBtn.onclick = () => finishGame(false);
switchBtn.onclick = () => finishGame(true);
function finishGame(shouldSwitch) {
if (gameFinished) return;
gameFinished = true;
let finalChoice = selectedDoor;
if (shouldSwitch) {
for (let i = 0; i < 3; i++) {
if (i !== selectedDoor && i !== revealedDoor) {
finalChoice = i;
}
}
}
const doors = document.querySelectorAll(".door");
for (let i = 0; i < 3; i++) {
doors[i].classList.add("open");
if (i === prizeDoor) {
doors[i].innerText = "🚗";
doors[i].classList.add("win");
} else {
doors[i].innerText = "🐐";
}
}
const win = finalChoice === prizeDoor;
stats.total++;
if (shouldSwitch) {
stats.switchTotal++;
if (win) stats.switchWin++;
} else {
stats.stayTotal++;
if (win) stats.stayWin++;
}
updateStats();
if (win) {
message.innerText = shouldSwitch
? "🎉 你换门后赢得了汽车!"
: "🎉 你坚持选择,赢得了汽车!";
} else {
message.innerText = shouldSwitch
? "😢 你换门后输了..."
: "😢 你坚持后输了...";
}
stayBtn.disabled = true;
switchBtn.disabled = true;
}
function updateStats() {
document.getElementById("total").innerText = stats.total;
document.getElementById("stayWin").innerText = stats.stayWin;
document.getElementById("stayTotal").innerText = stats.stayTotal;
document.getElementById("switchWin").innerText = stats.switchWin;
document.getElementById("switchTotal").innerText = stats.switchTotal;
const stayRate =
stats.stayTotal === 0
? 0
: ((stats.stayWin / stats.stayTotal) * 100).toFixed(1);
const switchRate =
stats.switchTotal === 0
? 0
: ((stats.switchWin / stats.switchTotal) * 100).toFixed(1);
document.getElementById("stayRate").innerText = stayRate + "%";
document.getElementById("switchRate").innerText = switchRate + "%";
}
resetGame();
</script>
</body>
</html>