이번에 다뤄볼 예제는 가장 매력적인 분야의 하나인 확률 시뮬레이션 분야입니다~ 컴퓨터 기술이 발전하면서 우리에게 가장 큰 이득을 가져다 준 분야가 시뮬레이션이지 않을까 싶은데요, 그 기반이 되는 것은 랜덤한 환경을 가정한 여러 번의 시뮬레이션을 거쳐서 평균 값들을 구하는 등 그 결과를 예측할 수 있게 된 것이라 할 수 있습니다.

가장 간단하게 프로그램으로 동전을 던져볼까요?

import random
from collections import Counter

#N개의 동전 던지기 결과를 리턴하는 함수
def coinFlip(N):
    return [random.randint(0, 1) for _ in range(N)]

random.seed(1)
>>> coinFlip(10)
[0, 1, 1, 0, 0, 0, 1, 1, 0, 0]

위와 같이 list의 형태로 앞면(0) 혹은 뒷면(1)의 동전 던지기 결과가 나오는 것을 확인할 수 있습니다. 위의 프로그램은 앞면과 뒷면의 확률을 50:50이라고 설정한 것인데, 그러면 실제로 그렇게 동전던지기 결과가 나오는지 확인해 볼까요?

>>> print Counter(coinFlip(10))
>>> print Counter(coinFlip(100))
>>> print Counter(coinFlip(1000))
>>> print Counter(coinFlip(10000))

Counter({0: 5, 1: 5})
Counter({1: 52, 0: 48})
Counter({0: 508, 1: 492})
Counter({0: 5016, 1: 4984})

적은 횟수를 시도할 때는 편차가 조금 심할 수는 있겠지만, 횟수가 많아질수록 점점 더 5:5에 가까워진다는 것을 확인하실 수 있겠죠? 이와 같은 방법으로 여러 번 랜덤한 변수를 발생시켜서 시뮬레이션 하는 방식을 Monte Carlo Simulation 방법이라고 합니다. 이것이 가능한 것은 결국 큰 수의 법칙(Law of Large Numbers) 때문이라고 할 수 있는데요, 큰 수의 법칙은 결국 임의의 랜덤 변수 라는 분포로부터 발생될때, \[\lim_{n\rightarrow\infty}\frac{1}{n}\sum_i X_i = E[X]\] 를 이용한 것입니다. 즉, 같은 분포로부터 많은 수를 발생시켜 단순히 평균을 취한 것은 실제 그 변수의 기대값과 같아진다는 법칙입니다.

이것도 프로그램으로 실제로 확인해 보죠. 주사위를 던진다고 가정해 봅시다. 모든 면은 의 확률로 동일하게 나오구요, 이 때 주사위의 눈(1~6)의 기대값을 구해보면, \[\frac{1}{6}\sum_{i=1}^6 i = 3.5\] 가 나옵니다. 이제 프로그램을 시켜 구해보게 하면,

#N개의 주사 던지기 결과를 리턴하는 함수
def rollingDice(N):
    return [random.randint(1, 6) for _ in range(N)]

>>> print sum(rollingDice(10))/10.0
>>> print sum(rollingDice(100))/100.0
>>> print sum(rollingDice(1000))/1000.0
>>> print sum(rollingDice(10000))/10000.0

3.7
3.67
3.428
3.5215

마찬가지로 큰 수의 법칙에 따라 점점 3.5로 수렴해 가는것을 확인할 수 있죠? 이러한 방법들을 잘 익히시면 복잡한 문제의 시뮬레이션도 쉽게 하실 수 있답니다~