2 minute read

몬티홀 문제(Monty Hall problem)

몬티홀 문제는 세계에서 가장 유명한 확률 역설입니다. 퀴즈쇼 사회자인 몬티 홀은 세 개의 문 중 하나의 뒤에 자동차가 있고, 두 개의 문 뒤에는 염소가 있다고 알려줍니다. 이때 참가자가 자동차를 고를 확률은 분명히 1/3이죠. 이때 자비로운 사회자가 문 하나를 선택하면 남아 있는 두 개의 문 중에서 염소가 있는 문 하나를 열어서 보여주고 다른 문을 선택할 기회를 한 번 더 줍니다.

이때 참가자는 문을 바꾸는게 좋은 선택일까요?? 아니면 사회자의 심리전에 불과할까요??

이미지 설명 1 이미지 설명 2 이미지 설명 3

이미지 설명 1 이미지 설명 2 이미지 설명 3

예시 파이썬 코드

다음은 참가자가 선택하지 않은 문 가운데 염소가 있는 문을 무작위로 열어주는 전략을 10만번 시뮬레이션 한 코드입니다.

## 일반 몬티홀 코드

import random

def money_hall_simulate(switch, num_trials):
    """
    몬티홀 문제 시뮬레이션 함수
    : switch - 참가자가 선택을 바꿀지 여부 (True/False)
    : num_trials - 시뮬레이션 횟수
    : return - 승리한 횟수
    """

    # 승리한 횟수 초기화
    win_count = 0
    for _ in range(num_trials):
        # 문 뒤에 상품이 있는 위치와 참가자의 선택을 무작위로 설정 (문은 1~3번이 있음)
        prize_door = random.randint(1,3)
        player_choice = random.randint(1,3)

        # 사회자가 염소가 있는 문을 열어줌(참가자의 선택과 상품의 위치가 같지 않은 문 중 하나를 무작위로 고름
        open_door = random.choice([i for i in range(1,4) if i != player_choice and i != prize_door])

        # 참가자가 선택을 바꾸기로 한 경우
        if switch:
            # 참가자는 처음 선택과 사회자가 열어준 문을 제외한 나머지 문을 선택
            player_choice = next(i for i in range(1,4) if i != player_choice and i != open_door)

        # 최종 선택이 상품이 있는 문이면 승리
        if player_choice == prize_door:
            win_count += 1
        
    return win_count # 승리 횟수 반환

# 시뮬레이션 실행
num_trials = 100000 # 10만번 시뮬레이션 반복
win_rate_no_switch = money_hall_simulate(False, num_trials) / num_trials # 선택을 바꾸지 않았을 경우 승리 확률
win_rate_switch = money_hall_simulate(True, num_trials) / num_trials # 선택을 바꾸었을 경우 승리 확률

# 결과 출력
print(f"선택을 바꾸지 않았을 때 승리할 확률: {win_rate_no_switch:.2f}")
print(f"선택을 바꾸었을 때 승리할 확률: {win_rate_switch:.2f}")
선택을 바꾸지 않았을 때 승리할 확률: 0.33
선택을 바꾸었을 때 승리할 확률: 0.67

그래프 시각화

시도 순서에 따른 승리 횟수를 시각화 하여 나타내 봅니다.

# ! pip install matplotlib
## 그래프 시각화

import matplotlib.pyplot as plt

# 한글 폰트 지정
plt.rcParams['font.family'] = 'Noto Sans KR'
plt.rcParams['axes.unicode_minus'] = False

# 시뮬레이션 실행 결과 저장 리스트 초기화
win_rates_no_switch = []
win_rates_switch = []

# 시뮬레이션 실행
trial_numbers = range(10, 100001, 100)

for num_trials in trial_numbers:
    win_rate_no_switch = money_hall_simulate(False, num_trials) / num_trials
    win_rates_no_switch.append(win_rate_no_switch)
    win_rate_switch = money_hall_simulate(True, num_trials) / num_trials
    win_rates_switch.append(win_rate_switch)

# 결과를 선 그래프로 그리기
plt.figure(figsize=(10, 6))
plt.plot(trial_numbers, win_rates_no_switch, color='#4B0082')
plt.plot(trial_numbers, win_rates_switch, color='#FF1493')

# 10만번 시뮬레이션 한 결괏값을 레이블로 표시
plt.text(70000, 0.36, f"선택을 바꾸지 않았을 때 \n승리 확률:{win_rates_no_switch[-1]:.2f}",
         ha='left', va='bottom', color='black', fontweight='bold', fontsize=10)
plt.text(70000, 0.6, f"선택을 바꾸었을 때 \n승리 확률:{win_rates_switch[-1]:.2f}",
         ha='left', va='bottom', color='black', fontweight='bold', fontsize=10)

plt.xlabel("시뮬레이션 횟수")
plt.ylabel("승리할 확률")
plt.show()

출처 : 수학은 알고 있다. (김종성_위니버스, 이택호 지음)