본문 바로가기
수학의 재미/확률분포

정균분포 난수를 만들어 주세요 #2 : Normal CDF 이용

by hustler78 2022. 6. 13.
728x90
반응형

이번글은 

2022.06.13 - [수학의 재미/확률분포] - 정균분포 난수를 만들어 주세요 #1 : 중심극한정리 이용

 

정균분포 난수를 만들어 주세요 #1 : 중심극한정리 이용

이번 글은 2022.06.10 - [수학의 재미/확률분포] - 균등난수(uniform random number) 만들어보자 #3 : Halton Sequence 균등난수(uniform random number) 만들어보자 #3 : Halton Sequence 이번 글은 2022.06.10 -..

sine-qua-none.tistory.com

에서 이어집니다. 정규분포를 만드는 두번째 방법! 에 대해 알아보겠습니다. 바로 정규분포의 Cumulative Distribution Function 을 이용하는 방법입니다.

방법1 중심극한 정리를 이용하여, uniform random number의 표본평균 구하기
방법2 Normal cumulative distribution function 을 이용하기
방법3 Box Muller 방법으로 정규분포 난수 2개를 한방에 구하기

표준정규분포를  따르는 확률변수 $Z\sim \mathcal{N}(0,1)$의 확률밀도함수(probability density function)과 누적분포함수(cumulative distribution function)은 다음과 같습니다.

$$
\begin{align}
\rm{pdf: ~} & ~\phi(x) = \frac1{\sqrt{2\pi}} \exp\left( -\frac{x^2}2 \right) \\
\rm{cdf: ~} &~ \Phi(x) = \int_{-\infty}^x \phi(z) dz =\int_{-\infty}^x\frac1{\sqrt{2\pi}} \exp\left( -\frac{z^2}2 \right) dz
\end{align}
$$

또한 cdf 의 정의에 따라

$$\Phi(x) = \mathbb{P}(Z\leq x) $$

이죠.

 $\Phi(x)$는 확률이므로 $0$과 $1$사이의 수입니다. 그리고 $x$는 표준평규분포를 의미하는 수이죠. 따라서

$$\Phi(x) = u~,~ u\in[0,1]$$

이면 $$ x= \Phi^{-1}(u) $$

입니다. 이 사실을 이용하여 정규분포 난수를 생성하는 것입니다. 즉,

1. 균등 난수(uniform random number)를 하나 추출한다. 이것을 $u$라 한다.
2. $\Phi^{-1}(u)$ 를 계산하여 정규분포 난수를 추출한다.

python으로 그래프를 한번 그려서 볼까요? 위의 과정은 아래 그림과 같습니다.

$u$에 해당하는 $x$좌표 찾기

아래는 위 그래프를 그리는 python code인데요. 제 개인적인 저장용이므로 참고만 하시면 됩니다.

 

import math
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

def plot_normcdfChart():
    x = np.arange(-5, 5, 0.05)
    y = norm.cdf(x, 0, 1)
    plt.plot(x, y)
    plt.hlines(y=0.6, xmin=-5, xmax=0.253347, linestyles='--', colors='lightgray')
    plt.vlines(x=0.253347, ymin=0, ymax=0.6, colors='lightgray', linestyles='-.')
    plt.text(4, 0.9, r'$y=\Phi(x)$')
    plt.text(-5, 0.65, r'$u$')
    plt.text(0.65, 0, r'$\Phi^{-1}(u)$')
    plt.plot(0.2533347, 0.6, 'o')
    plt.show()

if __name__ == '__main__':
    plot_normcdfChart()

 

그럼 이 사실을 이용하여 정규분포를 따르는 난수를 발생시켜 histogram을 그려보고 평균, 분산을 구해보겠습니다. python code는 다음과 같습니다.

 

import math
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

def normal_random_test_cdf():
    nSimulation = 5000
    rand_unif = np.random.uniform(low=0, high=1, size=nSimulation)
    data = norm.ppf(rand_unif)

    print('Average of data: {:4f}'.format(np.average(data)))
    print('Variance of data: {:4f}'.format(np.var(data)))

    plt.hist(data, bins=20)
    plt.show()

if __name__ == '__main__':
    normal_random_test_cdf()

간략하게 살펴보면 

    rand_unif = np.random.uniform(low=0, high=1, size=nSimulation)

uniform random number를 nSimulation = 5000 개만큼 추출해 rand_unif 변수에 저장합니다.

 

    data = norm.ppf(rand_unif)

norm.ppf라는 함수로 rand_unif를 계산한 결과를 data 변수에 저장합니다.

norm.ppf는 바로 $\Phi^{-1}$을 뜻하는 것으로서 정규분포 cdf의 역함수를 의미합니다. 

 

 

code 전체의 결과는 다음과 같습니다. histogram은

종모양의 정규분포 형태가 나옵니다. 평균과 분산은 각각

Average of data: -0.014075
Variance of data: 1.004006

Process finished with exit code 0

위와 같이 평균은 약 0, 분산은 1이 나오게 됩니다. 우리는 이렇게 정규분포 난수를 생성할 수 있었습니다. 그런데 python은 norm.ppf 라는 역함수를 내장 함수로 제공해주니까 쉬웠지, 그렇지 않으면 다른 방법을 써서 구해야 합니다. 만일 역함수를 제공해 주지 않았다면 어떻게 구할까요? 다음 글에서 알아보도록 하겠습니다.

728x90
반응형

댓글