이 포스팅에서는 지난 시간에 설명한 몬테 카를로 시뮬레이션 방법을 프로그램 언어 파이썬으로 구현해 보도록 하겠습니다. 적분은 몬테 카를로 시뮬레이션을 이용해서 할 수 있는 것의 가장 기본적인 어플리케이션입니다. 사실, 몬테 카를로 시뮬레이션은 "어려운 적분을 하기 위한 계산 방법론 이다" 라고 말해도 과언이 아닐 정도로 몬테 카를로 시뮬레이션의 적분에 대한 응용은 기본적이면서도 활용 범위가 넓습니다.
몬테 카를로 적분을 통한 계산 값이 정확하기 위해서는 몬테 카를로 시뮬레이션에서 하는 랜덤 샘플링의 횟수가 매우 커야 하는데요, 따라서 컴퓨터로 계산할 수 밖에 없습니다. 컴퓨터 프로그램 언어를 이용해서 원하는 알고리듬을 코딩 언어로 구현해야하는데, 프로그램 언어 중에서 배우기 쉽고, 활용 범위가 넓은 파이썬을 통해서 몬테 카를로 적분을 구현해 보도록 하겠습니다.
몬테 카를로 적분이란?
지난 포스팅에서 몬테 카를로 "시뮬레이션"의 일반에 대해서 설명하긴 했지만, 사실 다양한 몬테 카를로 시뮬레이션 중에서 가장 간단하면서도 핵심적인 몬테 카를로 "적분"을 설명하였습니다. 원주율값
(P-1) 원하는 적분 영역을 포함하는 사각형
(P-2) 이 사각형에 균일하게 랜덤 샘플링을 반복한다.
(P-3) 원하는 영역의 넓이
입니다. 샘플링이 삼각형 영역 안에서 균일하고, 랜덤 샘플링 횟수
구체적인 예를 들어서 설명해 보자

우리가 계산하고자 하는 영역은 위 그래프에서 파란색으로 색칠한 영역입니다.
(1)
(2)
(3)
로 둘러쌓인 영역입니다. 간단히 적분식으로 표현하면
이 문제에 대해서 위에서 몬테 카를로 적분의 과정 P-1, P-2, P-3 을 차례로 해 보도록 하겠습니다.
P-1 : 원하는 적분 영역을 포함하는 사각형을 정의하고, 원하는 영역의 안과 밖을 정의한다.
위 영역을 포함하는 직사각형을 잡으면 됩니다. 원리적으로는 아무런 모양의 직사각형을 잡아도 되지만, 계산의 효율성을 위해서는 해당 영역을 포함하는 최소 넓이의 직사각형을 선택하는것이 좋습니다. 직사각형의 가로의 길이는 자명하게 1이 되고, 세로의 길이는

그래프로 표현하면 이와 같습니다.

이때, 적분을 하려는 영역
P-2, P-3 : 이 사각형에 균일하게 랜덤 샘플링을 반복한다.
과정 P-2와 과정 P-3을 두 과정으로 분리하긴 좀 애매해서 한 번에 하도록 하겠습니다.
랜덤 샘플링을 한 점의 좌표를
(1) 만일
(2) 그렇지 않으면,
가 됩니다.
위에서 설명한 것과 같이 적분하려는 영역을 포함하는 넓은 영역에서 균일하게 샘플링을 하고, 선택된 점 중에서 원하는 영역에 속해 있는 점을 고르는 과정을 기각 샘플링(rejection sample)이라고 합니다. 기각 샘플링을 이용하여 보다 복잡한 분포를 갖는 확률 샘플링을 할 수 있는데, 이 포스팅에서 사용한 기각 샘플링은 그 중에서 가장 간단한 형태의 기각 샘플링 입니다.
이번 포스팅의 목표는 파이썬 프로그래밍을 통해서 실제로 위 값을 계산하는 것이니, 이 부분에 해당하는 파이썬 코딩을 해 보도록 하겠습니다. 다양한 방법으로 구현할 수 있지만, 나름 간단하게 구현하는 방법은 아래와 같습니다.
import numpy as np
from matplotlib import pyplot as plt
def function(x): ### 함수 정의
return x**2 + np.sin(np.pi*x)
if __name__ == '__main__':
N = int(1e+4) ### 랜덤 샘플링 시행 횟수 정의
W, H = 1, 1.4 ### 랜덤 샘플링을 할 사각형 R의 가로 세로 정의
X = np.random.random(N) ### 각 점의 x 좌표 랜덤 샘플링
Y = H*np.random.random(N) ### 각 점의 y 좌표 랜덤 샘플링
F = function(X) ### 각 랜덤 샘플링에 대한 f(x)를 계산
in_out = Y < F ### y와 f(x) 값 비교
A = H * W * np.sum(in_out) / N ### 영역 S의 넓이
print("A = ", A)
매우 짧은 코딩으로 몬테 카를로 적분을 구현하였습니다. 조금 더 효율적으로 짠다면 더 짧게 짤 수도 있지만 설명의 편의를 위해서 여러줄에 걸쳐 표현하였습니다.
파이썬의 numpy 라이브러리를 이용하여 구현하였습니다. np.random.random()을 활용하여 N 개의 랜덤 샘플 포인트 집합
마지막 줄은 영역
여기서 계산을 끝내도 되지만, 시각화를 할 수 있는 이미지를 출력하면 더 좋을 것 같습니다. 파이썬에서 자주 활용하는 그래프 그리는 라이브러리인 matplotlib을 통해서 그래프를 그려 보도록 하겠습니다.
color = list(map(lambda x: 'dodgerblue' if x else 'red', in_out))
### 샘플링 포인트의 색깔 설정
plt.scatter(X, Y, color=color, s=5, label='A = {}'.format(np.round(S, 4)))
### 샘플링 포인트 그리기
plt.plot([0, W], [0, 0], color='black') ### 사각형 영역 그리기
plt.plot([W, W], [0, H], color='black')
plt.plot([0, W], [H, H], color='black')
plt.plot([0, 0], [0, H], color='black')
px = np.linspace(0, W, 1000)
py = function(px)
plt.plot(px, py, color = 'black') ### 함수 y = f(x) 그리기
plt.legend(loc = 'lower right') ### 그래프 그리는 옵션 추가
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.show()
in_out 값이 True이면 하늘색으로, False이면 빨간색으로 점을 색칠할 것 입니다. 그 이후 부분은 matplotlib의 일반적인 사용법이라 사실 몬테 카를로 적분의 구현과는 큰 상관이 없습니다.
위 전체 프로그램을 실행하면

결과는 위와 같습니다. 1,000번 정도 샘플링을 하면 선택된 점은 전체 사각형 영역에 고르게 분포하게 됩니다. 이 때 적분값은 0.9796으로 실제 정확한 값 0.96700 대비 오차는 0.0126, 비율로 하면 1.3% 가 됩니다.
샘플링 횟수
if __name__ == '__main__':
N = int(1e+4)
W, H = 1, 1.4
As = []
for d in [1, 2, 3, 4, 5, 6]: ### 10^{1, 2, 3, 4, 5, 6}에 대한 계산 반복
N = 10**d
X = np.random.random(N)
Y = H*np.random.random(N)
F = function(X)
in_out = Y < F
A = H * np.sum(in_out) / N ### 넓이를 계산
As.append(A) ### 계산된 넓이를 As에 저장
plt.scatter([1, 2, 3, 4, 5, 6], As) ### d에 따른 A값을 그리기
plt.xlabel('Exponent')
plt.ylabel('A')
plt.axhline(0.967, color='gray', ls='--') ###참값 표시
plt.grid()
plt.show()
와 같이 간단히 구현할 수 있습니다. 앞에서 설명한 부분과 파이썬의 기본 문법인 For와 list 변수의 append를 사용했습니다.

결과는 위와 같습니다. 랜덤 샘플링의 횟수가 증가할 수록 몬테 카를로 적분값은 참값 (가로 점선)에 가까워 집니다. 무작위성이 들어가는 계산이기 때문에, 계산을 수행할 때 마다 얻어지는 값은 다릅니다. X축은 선형이 아니라 로그 스케일입니다.
모든 것은 일반화 되어야 한다.
몬테 카를로 적분 방법을 이용하여 1차원 함수
다음 포스팅에서는 효율적인 샘플링을 할 수 있는 방법 중에 하나인 메트로폴리스-헤이스팅스 알고리듬에 대해서 알아 봅니다.
https://studyingrabbit.tistory.com/35
메트로폴리스-헤이스팅스 알고리듬을 활용한 분포 생성 및 이를 이용한 몬테 카를로 적분
기각 샘플링 기반 몬테 카를로 적분이 부정확한 예시 지난 시간에 소개한 기각 샘플링 방법을 통해서
studyingrabbit.tistory.com
'수학' 카테고리의 다른 글
2차원 헬름홀츠 방정식(Helmholtz equation) 풀기 : 고유 함수의 개형, 고유 진동수 및 고유 진동수의 분포 (1) | 2021.08.16 |
---|---|
메트로폴리스-헤이스팅스 알고리듬을 활용한 분포 생성 및 이를 이용한 몬테 카를로 적분 (0) | 2021.08.15 |
몬테 카를로 시뮬레이션(Monte Carlo Simulation) 의 이해 : 원주율값 구하기 (+파이썬 시뮬레이션 코드) (5) | 2021.08.12 |
[선형대수-3, 고전역학-1] 행렬대각화를 이용한 결합진동자(coupled oscillator)의 운동방정식의 쉬운 풀이 (0) | 2021.01.09 |
[선형대수-2] 이차형식과 행렬 대각화 : 고유값에 따른 타원곡선의 결정 (8) | 2021.01.09 |