소프트웨어와 생활웹 교과서
이 페이지의 목차
4단원 · 6차시

시뮬레이션 구현하기 ① 확률

난수몬테카를로반복 실험

코드 예제: Python · 예상 50분

학습 목표

  • 무작위 수를 이용한 시뮬레이션을 구현할 수 있다.

관련 성취기준

  • [12소생04-04]

    소프트웨어를 적극적으로 활용하여 시뮬레이션 모델을 구현한다.

생각 열기 — 우리 반에 생일이 같은 친구가 있을까

컴퓨터가 우연을 흉내 내는 법 — 난수

난수(random number) — 미리 짐작할 수 없게 고르게 흩어져 나오는 수. 컴퓨터는 정해진 계산으로 난수처럼 보이는 수를 만든다는 짐작할 수 없게 나오는 수다. 파이썬에서는 random 모듈이 만들어 준다.

파이썬 random 모듈
코드하는 일쓰임
random.random()0 이상 1 미만의 소수 하나확률 20%로 일어나는 일: random.random() < 0.2
random.randint(1, 6)1부터 6까지 정수 하나 (6도 나온다)주사위 던지기
random.seed(4)난수의 «씨앗»을 정한다같은 씨앗이면 같은 난수가 같은 차례로 나온다 — 결과를 다시 보거나 확인할 때

몬테카를로 시뮬레이션

몬테카를로 방법(Monte Carlo method) — 난수로 같은 실험을 아주 많이 되풀이해 확률이나 값을 어림하는 방법. 이름은 모나코의 도박장 거리에서 따왔다은 난수로 같은 실험을 아주 많이 되풀이해 답을 어림하는 방법이다. 이름은 모나코의 도박장으로 유명한 곳 이름에서 따왔다. 공식을 몰라도, 너무 복잡해 공식으로 풀 수 없어도, «실험을 많이 하면» 답에 다가갈 수 있다.

점을 던져 원주율 구하기

한 변이 1인 정사각형에 점을 아무렇게나 던진다. 왼쪽 아래 꼭짓점을 중심으로 한 반지름 1인 사분원의 넓이는 π/4, 정사각형의 넓이는 1이다. 그러니 사분원 안에 든 점의 비율 × 4 ≈ π 다.

시뮬레이터점을 던져 원주율 어림하기

출력

원주율 ≈ 4 × (사분원 안에 든 점) ÷ (던진 점). 정사각형 넓이는 1, 사분원 넓이는 π/4 이기 때문이다.

점을 던지면 어림값이 어떻게 움직이는지 그려진다.

시뮬레이터

원주율 어림하기

약 10분
  1. +10 을 세 번 눌러 보자. 어림값이 3.14 에 가까운가?
  2. +1,000 과 +100,000 을 눌러 보자. 그래프의 선은 어떻게 움직이는가?
  3. 옆 친구의 값과 견주어 보자. 같은가? 둘 다 3.14 근처인가?

같은 실험을 파이썬으로 하면 이렇다.

Python몬테카를로로 원주율 어림하기
import random

random.seed(4)
for n in [100, 1000, 10000, 100000]:
    안 = 0
    for _ in range(n):
        x, y = random.random(), random.random()
        if x * x + y * y <= 1:
            안 += 1
    print(n, 4 * 안 / n)
확인된 실행 결과
100 2.84
1000 3.152
10000 3.1368
100000 3.14204

100개만 던지면 2.84 로 꽤 틀리지만, 10만 개를 던지면 3.142 로 소수 셋째 자리까지 맞는다. 다만 10,000개일 때(3.1368)가 1,000개일 때(3.152)보다 오히려 조금 더 멀다 — 난수라서 늘 차근차근 다가가는 것은 아니고, 크게 보면 다가간다. 많이 되풀이할수록 어림값이 참값에 가까워지는 성질을 대수의 법칙(law of large numbers) — 같은 시행을 많이 되풀이할수록 결과의 비율이 실제 확률에 가까워진다는 성질이라 한다.

생일 문제

이제 생각 열기의 물음으로 돌아가자. 365일 가운데 아무 날이나 생일로 뽑아 반을 만들고, 생일이 같은 사람이 있는지 본다. 이것을 5,000번 되풀이해 «있었던 비율»을 센다.

Python생일 문제 — 반마다 5,000번 실험
import random

random.seed(7)

def 같은생일있나(사람):
    생일 = [random.randint(1, 365) for _ in range(사람)]
    return len(set(생일)) < 사람   # 겹치면 집합이 작아진다

for 사람 in [10, 23, 30, 40]:
    횟수 = 5000
    있음 = sum(같은생일있나(사람) for _ in range(횟수))
    print(f'{사람}명: {있음 / 횟수:.3f}')
확인된 실행 결과
10명: 0.110
23명: 0.510
30명: 0.703
40명: 0.884

23명만 모여도 생일이 같은 두 사람이 있을 확률이 절반쯤이다. 많은 사람이 짐작보다 훨씬 크다고 놀란다. 공식으로도 풀 수 있지만(23명이면 약 0.507), 공식을 몰라도 실험을 되풀이해 거의 같은 답을 얻었다. 이것이 몬테카를로 시뮬레이션의 힘이다.

그래프로 보기 — 주사위 두 개

주사위 두 개를 10,000번 던져 눈의 합이 몇이 몇 번 나오는지 히스토그램으로 그린다(Ⅲ단원의 맷플롯립).

Python주사위 두 개 눈의 합
import random
import matplotlib.pyplot as plt

random.seed(1)
합들 = [random.randint(1, 6) + random.randint(1, 6) for _ in range(10000)]

plt.figure(figsize=(6, 3.5))
plt.hist(합들, bins=range(2, 14), align='left', rwidth=0.8)
plt.title('주사위 두 개 눈의 합 — 10,000번')
plt.xlabel('눈의 합')
plt.ylabel('나온 횟수')
plt.xticks(range(2, 13))
plt.show()
print('7이 나온 비율:', 합들.count(7) / 10000)
확인된 실행 결과
7이 나온 비율: 0.1675
주사위 두 개 눈의 합 실행 결과 그림 1

7이 가장 많이 나온다. 7을 만드는 방법이 (1,6) (2,5) (3,4) (4,3) (5,2) (6,1) 여섯 가지로 가장 많기 때문이다. 이론으로는 6/36 ≈ 0.167 이다.

실습

나만의 확률 실험

약 20분

위 예제 가운데 하나를 고치기 해서 새 물음에 답해 보자.

  • 생일 문제에서 우리 반 학생 수를 넣으면?
  • 주사위 세 개의 합은 어떤 모양이 될까?
  • 동전 10개를 던져 앞면이 7개 이상 나올 확률은?
활동지

확률 실험 기록

스스로 점검
  • 짐작과 결과를 견주었다
  • 새 물음과 결과를 적었다
  • 되풀이 횟수의 영향을 적었다

글자 규칙으로 살펴보는 점검표다. 다 켜졌다고 좋은 답이 되는 것은 아니다 — 마지막 판단은 내가 한다.

적은 내용은 이 브라우저에만 저장된다. 다른 컴퓨터로 옮기거나 제출하려면 내려받는다.

확인 문제

개념 확인 문제

보기를 누르면 바로 채점된다. 틀려도 해설을 읽으면 된다.

파이썬에서 1부터 6까지 정수 하나를 무작위로 고르는 코드는?12소생04-04

점 던지기로 원주율을 어림할 때 「사분원 안에 든 점의 비율 × 4」를 쓰는 까닭은?12소생04-04

몬테카를로 시뮬레이션에서 되풀이 횟수를 늘리면 일반적으로 어떻게 되는가?12소생04-04

random.seed(7) 을 넣어 두는 까닭으로 알맞은 것은?12소생04-04

이 차시는 C 예제를 제공하지 않는다. 상단에서 다른 언어로 바꾸면 예제를 볼 수 있다.

교과서에서 찾기

이 차시에 대응하는 교과서 위치가 아직 정리되지 않았다. [확인필요]