import random
from collections import Counter
def monty_hall_game(switch_door):
# 初始化门后面的情况:一只羊在随机门后
doors = [1, 2, 3]
prize_door = random.choice(doors)
# 玩家第一次选择
first_choice = random.choice(doors)
# 主持人排除一个错误门
remaining_doors = [door for door in doors if door != first_choice and door != prize_door]
host_opens = random.choice(remaining_doors)
# 是否更换选择
if switch_door:
final_choice = [door for door in doors if door != first_choice and door != host_opens][0]
else:
final_choice = first_choice
# 判断是否获胜
return final_choice == prize_door
# 模拟游戏
def simulate_games(num_games):
# 不换门的策略
stay_results = Counter(monty_hall_game(False) for _ in range(num_games))
# 换门的策略
switch_results = Counter(monty_hall_game(True) for _ in range(num_games))
print(f"模拟{num_games}次游戏的结果:")
print(f"不换门的胜率: {stay_results[True]/num_games*100:.2f}%")
print(f"换门的胜率: {switch_results[True]/num_games*100:.2f}%")
# 运行模拟
simulate_games(10000)
package main
import (
"fmt"
"math/rand"
"time"
)
// 模拟单次游戏
func playMontyHall(switchDoor bool) bool {
// 初始化三扇门 (1-3)
doors := []int{1, 2, 3}
// 随机放置奖品
prizeDoor := doors[rand.Intn(3)]
// 玩家第一次选择
firstChoice := doors[rand.Intn(3)]
// 主持人排除一个错误门
var hostOpens int
for _, door := range doors {
if door != firstChoice && door != prizeDoor {
hostOpens = door
break
}
}
// 根据策略决定最终选择
var finalChoice int
if switchDoor {
// 换门:选择剩下的那扇门
for _, door := range doors {
if door != firstChoice && door != hostOpens {
finalChoice = door
break
}
}
} else {
// 不换门:保持第一次的选择
finalChoice = firstChoice
}
// 返回是否获胜
return finalChoice == prizeDoor
}
// 模拟多次游戏并统计结果
func simulateGames(numGames int) {
stayWins := 0
switchWins := 0
for i := 0; i < numGames; i++ {
if playMontyHall(false) {
stayWins++
}
if playMontyHall(true) {
switchWins++
}
}
stayWinRate := float64(stayWins) / float64(numGames) * 100
switchWinRate := float64(switchWins) / float64(numGames) * 100
fmt.Printf("模拟%d次游戏的结果:\n", numGames)
fmt.Printf("不换门的胜率: %.2f%%\n", stayWinRate)
fmt.Printf("换门的胜率: %.2f%%\n", switchWinRate)
}
func main() {
// 设置随机数种子
rand.Seed(time.Now().UnixNano())
// 运行模拟
simulateGames(10000)
}
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MontyHallSimulation {
private static final Random random = new Random();
/**
* 模拟单次游戏
* home.php?mod=space&uid=275307 switchDoor 是否选择换门
* home.php?mod=space&uid=161696 是否获胜
*/
private static boolean playMontyHall(boolean switchDoor) {
// 初始化三扇门 (1-3)
List<Integer> doors = new ArrayList<>();
for (int i = 1; i <= 3; i++) {
doors.add(i);
}
// 随机放置奖品
int prizeDoor = doors.get(random.nextInt(3));
// 玩家第一次选择
int firstChoice = doors.get(random.nextInt(3));
// 主持人排除一个错误门
int hostOpens = doors.stream()
.filter(door -> door != firstChoice && door != prizeDoor)
.findFirst()
.orElse(0);
// 根据策略决定最终选择
int finalChoice;
if (switchDoor) {
// 换门:选择剩下的那扇门
finalChoice = doors.stream()
.filter(door -> door != firstChoice && door != hostOpens)
.findFirst()
.orElse(0);
} else {
// 不换门:保持第一次的选择
finalChoice = firstChoice;
}
// 返回是否获胜
return finalChoice == prizeDoor;
}
/**
* 模拟多次游戏并统计结果
* @param numGames 模拟次数
*/
private static void simulateGames(int numGames) {
int stayWins = 0;
int switchWins = 0;
for (int i = 0; i < numGames; i++) {
if (playMontyHall(false)) {
stayWins++;
}
if (playMontyHall(true)) {
switchWins++;
}
}
double stayWinRate = (double) stayWins / numGames * 100;
double switchWinRate = (double) switchWins / numGames * 100;
System.out.printf("模拟%d次游戏的结果:%n", numGames);
System.out.printf("不换门的胜率: %.2f%%%n", stayWinRate);
System.out.printf("换门的胜率: %.2f%%%n", switchWinRate);
}
public static void main(String[] args) {
// 运行模拟
simulateGames(10000);
}
}
第一次选择时,选中正确门的概率是 1/3
如果坚持第一次选择,胜率保持在 1/3
如果选择更换,胜率会变成 2/3
为什么换门会有更高的胜率?
初始选择错误的概率是 2/3
主持人会帮你排除一个错误选项
如果你的初始选择是错的(概率 2/3),那么换门必定会赢
如果你的初始选择是对的(概率 1/3),那么换门必定会输
结论:
从概率学角度来看,选择更换门是更优的策略
实际模拟也证明,换门策略的胜率接近 66.67%
不换门策略的胜率接近 33.33%
这个问题之所以反直觉,是因为大多数人倾向于认为排除一个错误选项后,剩下两个选项的概率应该是相等的(各50%)。但实际上,主持人的行为提供了额外的信息,改变了概率分布。
|