본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 07. 16:54

Python으로 〇× 게임 AI를 밑바닥부터 만들기 그 231: 대수의 법칙과 중심극한정리의 관계

요약

Python을 이용한 〇× 게임 AI 개발 과정 중, 통계학의 핵심인 대수의 법칙과 중심극한정리의 관계를 설명합니다. 표본 평균의 분포가 표본 크기가 커짐에 따라 정규 분포로 수렴하는 원리를 다룹니다.

핵심 포인트

  • 대수의 법칙은 표본 평균이 모평균에 수렴함을 의미함
  • 중심극한정리는 표본 평균의 분포가 정규 분포로 근사함을 설명함
  • 중심극한정리는 원래 분포와 상관없이 유한한 분산을 가지면 성립함
  • 표본 크기 n이 충분히 커지면(일반적으로 30 이상) 정규 분포 근사가 가능함

본 기사의 프로그램은 Python 버전 3.13에서 실행하고 있습니다. 또한, numpy 버전은 2.3.5입니다.

링크설명
marubatsu.pyMarubatsu, Marubatsu_GUI 클래스 정의
...
AI 목록과 지금까지 작성한 데이터 파일에 대해서는 아래 기사를 참조해 주세요.

다음 기사에서 설명할 예정인 원시 몬테카를로 법(Primitive Monte Carlo method)을 이용한 〇× 게임 AI 작성에는 필요하지 않지만, 대수의 법칙(Law of Large Numbers)에 대해 지금까지 자세히 설명했으므로, 대수의 법칙과 밀접하게 관련된 통계학에서 가장 중요한 정리 중 하나인 중심극한정리(Central Limit Theorem)에 대해 설명하겠습니다.

대수의 법칙은 모집단에서 무작위 복원 추출된 표본의 표본 평균이 표본 크기를 키우면 모평균에 가까워진다(확률 수렴한다)는 것이지만, 표본 크기를 키웠을 때 표본 평균의 분포가 어떻게 변화하는지에 대해서는 언급되어 있지 않습니다. 그 점을 구체적으로 나타내는 정리가 중심극한정리입니다.

중심극한정리의 정의를 요약하면 다음과 같습니다.

유한한 값을 갖는 모평균 $\mu$와 모분산 $\sigma^2$을 가진 모집단의 확률 분포를 나타내는 확률 변수를 $X$라고 하고, 그 모집단으로부터 $n$개의 요소를 무작위 복원 추출한다고 하자. 생성된 표본 크기가 $n$인 표본의 표본 평균을 나타내는 확률 변수를 $\bar{X_n}$이라고 하면, $n$을 충분히 크게 하면 $\bar{X_n}$이 따르는 확률 분포는 기대값이 $\mu$, 분산이 $\frac{\sigma^2}{n}$인 정규 분포(Normal Distribution)로 근사할 수 있다.

$\bar{X_n}$의 기대값 $E[\bar{X_n}]$이 $\mu$라는 것은 이전 기사에서, 분산 $V[\bar{X_n}]$이 $\frac{\sigma^2}{n}$이라는 것은 이전 기사에서 증명되었습니다. 중심극한정리에서는 그 점에 더해, 표본 평균의 확률 분포의 형태가 정규 분포에 가까워진다는 것을 보여주며, 이러한 성질을 분포 수렴(Distribution Convergence)이라고 부릅니다.

위에서 "$n$을 충분히 크게 하면"이라는 애매한 표현을 사용한 이유는, 원래의 확률 분포에 따라 얼마나 $n$이 커져야 정규 분포로 근사할 수 있는지가 다르기 때문입니다. 참고로 일반적으로는 $n$이 30 이상이 되면 정규 분포로 근사해도 좋다고 말하는 경우가 많은 것 같습니다.

헷갈리기 쉽지만, 중심극한정리에 의해 정규 분포에 가까워지는 것은 원래의 $X$의 확률 분포가 아니라, 표본 평균 $\bar{X_n}$의 확률 분포라는 점에 주의하십시오.

당연한 이야기지만 표본 크기를 아무리 키운다고 해서 원래의 확률 분포가 변하는 것은 아닙니다. 예를 들어 주사위를 몇 번 던져서 그 눈을 기록(무작위 복원 추출)하더라도, 주사위를 던졌을 때의 눈의 확률은 변하지 않습니다.

중심극한정리가 매우 편리한 점은 유한한 기대값과 분산을 갖는다는 조건을 만족하면 원래의 확률 분포가 어떤 분포이든 상관없이 성립한다는 점에 있습니다. 어떻게 편리한지에 대한 구체적인 예시는 이번 기사의 후반부에서 보여드리겠습니다.

또한, 중심극한정리의 증명은 매우 어렵기 때문에 이번 기사에서는 중심극한정리가 다양한 확률 분포에 대해 성립함을 확인하도록 하겠습니다.

대수의 법칙은 확률 분포가 유한한 분산을 갖는 것을 조건으로 하지 않지만, 중심극한정리는 유한한 분산을 갖는 것을 조건으로 한다는 점이 다릅니다.

다만, 유한한 분산을 갖지 않는 경우에도 대수의 법칙이 만족되는 것을 상상할 수 있듯이, 확률 분포가 유한한 기대값을 갖는다는 조건을 만족하면 정규 분포와는 다른 특정 확률 분포로 분포 수렴합니다. 또한, 유한한 분산을 갖는다는 조건을 필요로 하지 않는 경우의 중심극한정리를 일반화 중심극한정리(Generalized Central Limit Theorem)라고 부릅니다.

참고로 Wikipedia의 중심극한정리 항목을 아래에 제시합니다.

표본 크기 $n$을 키우면 표본 평균을 나타내는 확률 변수 $\bar{X_n}$의 변동성을 나타내는 분산이 어떻게 변화하는지 설명하겠습니다. 이전 기사에서 증명한 것처럼, 표본 크기가 $n$인 표본 평균의 분산 $V[\bar{X_n}]$은 아래 식과 같이 모분산 $\sigma^2$을 $n$으로 나눈 $\frac{\sigma^2}{n}$이 됩니다.

$V[\bar{X_n}] = \frac{V[X]}{n} = \frac{\sigma^2}{n}$

중심극한정리의 전제 조건으로부터 $\sigma^2$은 유한한 값이므로, 표본 크기 $n$을 키워 나가면 $V[\bar{X_n}]$의 극한값은 아래 식과 같이 0이 됩니다.

$\lim_{n \to ∞} V[\bar{X_n}] = \lim_{n \to ∞} \frac{σ^2}{n} = 0$

이전 기사에서 설명했듯이, 분산 (Variance)이 0이라는 것은 변동성 (Variability)이 전혀 없다(모든 실현값이 평균과 같아진다)는 것을 나타냅니다. 표본 평균 $\bar{X_n}$의 기댓값 $E[\bar{X_n}]$은 항상 모평균 (Population Mean)인 $\mu$와 일치하므로, 이 식은 대수의 법칙 (Law of Large Numbers)의 메커니즘을 다음과 같이 설명해 줍니다.

  • $n$을 크게 하면 표본 평균의 변동성을 나타내는 $V[\bar{X_n}]$이 점점 0을 향해 작아진다
  • 그 결과, 표본 평균 $\bar{X_n}$의 실현값이 모평균 $\mu$의 매우 가까운 곳에 집중되게 된다
  • 따라서 표본 크기 (Sample Size) $n$을 충분히 크게 잡으면, 1회의 표본 추출 (Sampling)으로 얻을 수 있는 표본의 표본 평균은 거의 확실하게 모평균 $\mu$ 부근의 값을 취한다 (대수의 법칙)

위의 내용이 성립함을 프로그램 시뮬레이션을 통해 확인해 보기로 하겠습니다. 이를 위해 아래의 계산을 수행하는 sampling_and_analyze 함수를 정의하겠습니다. 표본 추출 (Sampling)과 분석 (Analyze)을 수행하므로 그러한 명칭을 붙였습니다.

  • 지정한 확률 분포 (Probability Distribution)의 모분산 (Population Variance) $\sigma^2$을 계산한다
  • 해당 확률 분포로부터 무작위 복원 추출 (Sampling with Replacement)을 통해 표본 크기가 size인 표본을 num개 생성한다
  • 생성한 num개 표본의 표본 평균을 계산한다
  • num개 표본 평균의 분산을 계산한다
  • 모분산과 $\frac{\sigma^2}{n}$을 나타내는 「표본 평균의 분산 $\div$ size」를 비교한다

아래는 sampling_and_analyze의 가변 인자 (Argument) 목록입니다. XP는 이전 기사에서 정의한 create_pd가 생성하는 확률 분포 데이터와 동일한 것입니다. 참고로 용어가 비슷하여 혼동하기 쉬우나, 「표본 크기 (Sample Size)」는 하나의 표본 안에 있는 요소의 수를, 「표본 수 (Number of Samples)」는 생성한 표본의 개수를 나타낸다는 점에 주의해 주세요.

가변 인자의미
X확률 변수 (Random Variable) $X$가 가질 수 있는 값의 목록을 나타내는 1차원 ndarray
PX의 각 요소 값이 발생할 확률
size생성할 표본의 표본 크기
num생성할 표본 수

먼저 XP로부터 모평균 $\mu = E[X]$와 모분산 $\sigma^2 = V[X]$를 계산해야 하므로, 그 계산 방법에 대해 설명하겠습니다.

모평균은 numpy의 np.average 함수로 계산할 수 있습니다. np.average는 지정된 ndarray 요소의 평균 (Average)을 계산하는 함수이지만, 이때 가변 인자 weights에 각 요소의 가중치 (Weight)를 대입함으로써 「각 요소에 가중치를 곱한 값의 합계」 $\div$ 「가중치의 합계」라는, 가중치를 고려한 평균값을 계산합니다.

np.average의 상세 내용은 아래 링크를 참조해 주세요.

아래 프로그램의 첫 번째 줄은 1과 2의 평균인 1.5를 계산하지만, 두 번째 줄은 $1 \times 1$과 $2 \times 3$의 합계인 7을 가중치의 합계인 4로 나눈 1.75를 계산합니다.

import numpy as np
print(np.average([1, 2]))
print(np.average([1, 2], weights=[1, 3]))

실행 결과

1.5
1.75

확률 분포의 기댓값은 확률 분포의 각 요소 값과 그 확률을 곱한 값의 합계로 계산할 수 있으며, 확률을 나타내는 P의 합계는 1이 되므로, np.average의 가변 인자 weightsP를 대입함으로써 XP로 표현되는 확률 분포의 기댓값을 계산할 수 있습니다. 아래는 앞면이 0, 뒷면이 1인 공정한 동전을 던졌을 때의 확률 분포 기댓값을 계산하는 프로그램입니다. 실행 결과로부터 0.5라는 올바른 계산이 이루어짐을 확인할 수 있습니다.

X = np.array([0, 1])
P = np.array([0.5, 0.5])
print(np.average(X, weights=P))

이전 기사에서 설명했듯이, 분산은 아래 공식으로 계산할 수 있습니다. 이 중 $E[X]$는 위의 프로그램으로 계산할 수 있습니다.

$V[X] = E[X^2] - E[X]^2$

$V[X] = E[(X- E[X])^2]$ 로도 계산할 수 있습니다. 관심이 있으신 분은 이 식으로 계산해 보시기 바랍니다.

$E[X^2]$ 는 앞서 사용한 X 대신, X의 각 요소를 2제곱하는 X ** 2를 기술한 아래 프로그램으로 계산할 수 있습니다. X * X[0 * 0, 1 * 1] = [0, 1] 이므로 그 기댓값은 위와 동일한 0.5가 되며, 실행 결과로부터 올바른 계산이 수행됨을 확인할 수 있습니다.

print(np.average(X ** 2, weights=P))

실행 결과

0.5

따라서, XP로 나타내어지는 확률 분포의 분산은 아래 프로그램으로 계산할 수 있습니다. 실행 결과로부터 $0.5 - 0.5^2 = 0.25$ 가 올바르게 계산됨을 확인할 수 있습니다.

print(np.average(X ** 2, weights=P) - np.average(X, weights=P) ** 2)

실행 결과

0.25

다음으로, 무작위 복원 추출을 통해 표본 크기(sample size)가 size인 표본을 num개 생성하고, 그 표본 평균의 분산을 계산하는 방법을 설명하겠습니다.

이전 글에서 설명한 바와 같이, 무작위 복원 추출에 의한 표본 크기가 size인 표본은 아래 프로그램으로 계산할 수 있습니다.

np.random.choice(X, p=P, size=size)

위의 프로그램을 num번 반복함으로써 이 표본을 num개 생성할 수도 있지만, 아래 프로그램과 같이 가변 인자(keyword argument) size(size, num)이라는 튜플(tuple)을 대입함으로써, num개의 표본을 나타내는 (size, num) 형태(shape)를 가진 2차원 ndarray를 한 번의 np.random.choice로 묶어서 계산할 수도 있습니다.

size = 5
num = 10
sample = np.random.choice(X, p=P, size=(size, num))
...

실행 결과

[[0 1 0 0 0 0 0 0 0 1]
[0 1 0 1 0 1 0 1 0 0]
[1 1 0 1 1 1 0 0 0 1]
...

이전 글에서 설명한 것처럼, 다차원 ndarray의 각각의 인덱스를 축(axis)이라고 부르며, 맨 앞부터 axis 0, axis 1, ... 과 같이 번호로 구분하여 표기합니다. 위의 실행 결과에서는 세로 방향이 axis 0에, 가로 방향이 axis 1에 대응합니다.

위에서 계산된 2차원 ndarray는 첫 번째 인덱스를 나타내는 axis 0이 size개 있는 표본의 요소 중 몇 번째 요소인지를 나타내고, 다음 인덱스를 나타내는 axis 1이 num개 있는 표본 중 몇 번째 표본인지를 나타냅니다. 예를 들어 sample[0][0]은 0번 표본의 0번 요소를, sample[1][5]는 5번 표본의 1번 요소를 나타냅니다. 또한, 요소의 번호는 0부터 세므로 첫 번째 요소는 0번이라는 점에 주의하십시오. 따라서 위의 2차원 ndarray 데이터로부터 표본의 각 요소를 나타내는 axis 0을 대상으로 평균을 계산함으로써, 10개 표본의 평균을 한꺼번에 계산할 수 있습니다.

이전 글에서 설명한 np.flip과 마찬가지로, numpy의 많은 함수는 가변 인자 axis에 값을 대입함으로써 특정 axis를 대상으로 한 계산을 수행할 수 있습니다. np.average의 경우 아래 프로그램과 같이 가변 인자 axis0을 대입함으로써 axis 0을 대상으로 한 평균을 계산하며, 실행 결과에는 10개 표본의 표본 평균 목록이 계산된 ndarray가 표시됩니다. 앞서 확인한 실행 결과의 각 열(세로 방향으로 나열된 5개의 요소)의 평균이 각각 계산되고 있음을 확인하십시오. 또한, 요소의 평균(mean)을 계산하면 되므로, 앞서와 달리 가중치를 나타내는 가변 인자 weights에 값을 대입할 필요는 없습니다.

mean = np.average(sample, axis=0)
print(mean)

실행 결과

[0.4 0.8 0.2 0.8 0.4 0.4 0.4 0.4 0. 0.6]

np.average와 유사한 함수로 np.mean이 있습니다. np.mean은 가중치를 고려한 계산을 수행할 수 없다는 점이나, 마스크가 지정된 ndarray (Masked ndarray)에 대응한다는 점 등이 다릅니다.

np.mean에 대한 자세한 내용은 아래 링크를 참조해 주세요.

여러 개의 표본을 한꺼번에 계산하는 것이 처리 속도가 더 빠릅니다. 아래는 sizenum을 100으로 설정했을 때, 한꺼번에 계산하는 경우와 100회의 반복 처리를 통해 100개의 표본을 생성하는 경우의 처리 시간을 측정하는 프로그램입니다. 실행 결과로부터, 한꺼번에 계산하는 편이 처리 속도가 10배 이상 빨라진다는 것을 확인할 수 있었습니다.

size = 100
num = 100
%timeit np.random.choice(X, p=P, size=(size, num))
...

실행 결과

176 μs ± 461 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
2.26 ms ± 32.3 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

1차원 ndarray의 분산은 아래 프로그램과 같이 np.var라는 함수로 계산할 수 있습니다.

print(np.var(mean))

실행 결과

0.05600000000000001

np.var에 대한 자세한 내용은 아래 링크를 참조해 주세요.

아래는 위에서 설명한 방법으로 「모평균과 모분산의 계산」, 「여러 개의 표본에 대한 무작위 복원 추출」, 「각 표본의 표본 평균과 그 분산의 계산」을 수행하는 sampling_and_analyze를 정의하는 프로그램입니다.

1행: 방금 설명한 가변 인자를 가진 함수를 정의한다 -
2행: 표본 크기를 표시한다 -
3~6행: 모평균과 모분산을 계산하여 표시한다 -
7, 8행: 표본 크기가 size인 표본을 num개 추출하고, num개의 표본 평균을 계산한다 -
9행: 표본 평균의 분산을 계산한다 -
10~12행: 표본 평균의 분산, 모분산 ÷ 표본 크기, 이 두 가지의 오차를 표시한다. 또한, 오차는 절대값을 계산하는 np.abs를 이용하여 양수 값으로 표시하도록 했다

1 def sampling_and_analyze(X, P, size, num):
2 print(f"표본 크기 = {size}")
3 E = np.average(X, weights=P)
...

행 번호가 없는 프로그램

def sampling_and_analyze(X, P, size, num):
print(f"표본 크기 = {size}")
E = np.average(X, weights=P)
...

np.abs에 대한 자세한 내용은 아래 링크를 참조해 주세요.

위의 프로그램은 7행에서 shape이 (size, num)인 2차원 ndarray를 생성합니다. 그 ndarray의 요소 수는 size * num이므로, 예를 들어 표본 크기를 1만, 표본 수를 1만으로 설정할 경우에는 1억 개의 요소를 가진 ndarray가 생성되어 버려, 지난 기사에서 설명한 것과 마찬가지로 컴퓨터의 메모리 부족이 발생할 가능성이 있습니다.

그래서 지난 기사와 마찬가지로, 한 번에 생성하는 ndarray의 요소 수를 제한하여 분할 처리를 수행하도록 개량하기로 합니다.

지난 기사에서는 한 번의 처리로 하나의 표본만을 추출했었기 때문에, 한 번에 추출하는 표본의 요소 수를 100만 개로 제한했습니다. 이번 기사에서는 여러 개의 표본을 한꺼번에 추출한다는 점이 다르지만, 처음에는 표본 크기에만 주목하여 지난 기사와 마찬가지로 한 번에 추출하는 표본의 요소 수를 특정 수로 제한하는 분할 처리를 수행하기로 합니다.

아래는 한 번의 처리로 추출하는 표본의 요소(element) 수의 상한을 나타내는 변수 maxe의 값에 따른 분할 처리를 수행하는 프로그램입니다. 또한, 그때 표본 크기 size = 8, 표본 수 num = 3, maxe = 5를 구체적인 예시로 설명합니다. 확률 분포는 앞서 언급한 치우침이 없는 동전을 던졌을 때의 것을 이용했습니다.

sizelefts

를 계산하는 처리는 지난 기사의 프로그램과 동일하지만, 이 수정을 통해 표본의 요소를 분할하여 추출하게 되므로 표본 평균을 np.average로 계산할 수 없게 됩니다. 따라서 분할한 표본 요소의 합계를 계산하고, 마지막에 표본 크기(sample size)로 나누어 표본 평균을 계산해야 합니다.

4행: 추출해야 할 남은 표본 요소 수를 나타내는 변수 sizeleftsize로 초기화한다.

5행: num개 각 표본 요소의 합계를 계산하는 변수를 이전 기사에서 설명한 np.zeros를 이용하여, 요소 수가 num이고 모든 값이 0인 1차원 ndarray로 초기화한다.

6~15행: sizeleft가 0보다 큰 경우 반복 처리를 수행함으로써, 필요한 표본 크기의 표본을 분할 처리로 추출한다.

7, 8행: 한 번에 추출할 표본의 표본 크기를 나타내는 s를 계산하고, 그 표본 크기로 num개의 표본을 한꺼번에 추출한다.

9~13행: 계산의 중간 과정을 확인할 수 있도록, 추출한 데이터와 추출한 데이터 내의 각각의 표본 합계를 표시한다. 다음 반복 처리를 구분할 수 있도록 13행에서 빈 줄을 표시하도록 했다. 또한, 추출한 데이터 내 표본의 합계는 방금 표본 평균을 계산했던 np.average 대신 np.sum을 기술함으로써 계산할 수 있다.

14행: total에 추출한 각각의 표본 합계를 가산한다.

15행: s개의 요소를 추출했으므로, sizeleft에서 그 값을 뺀다.

16행: 반복 처리가 종료된 시점에 total에 각 표본 요소의 합계가 계산되므로 이를 표시한다.

17, 18행: 각 표본 요소의 합계를 표본 크기로 나누어 각 표본의 평균을 계산하여 표시한다.

19, 20행: np.var로 표본 평균의 분산(variance)을 계산하여 표시한다.

1 size = 8
2 num = 3
3 maxe = 5
...

행 번호가 없는 프로그램

size = 8
num = 3
maxe = 5
...

실행 결과

sample
[[1 1 0]
[1 0 1]
...

s = min(sizeleft, maxe) = min(8, 5) = 5이므로, 첫 번째 반복의 8행 처리에서는 "5개의 요소를 가진 3개의 표본"을 나타내는 (5, 3) shape의 ndarray가 계산되며, 그 값이 위 실행 결과의 첫 번째 sample로 표시됩니다. 또한, 3개 표본 요소의 합계가 그 아래의 sum에 올바르게 계산되어 있음을 확인할 수 있습니다.

위와 같은 추출을 수행한 결과, 추출해야 할 남은 표본 요소 수는 sizeleft = 8 - 5 = 3이 됩니다. s = min(sizeleft, maxe) = min(3, 5) = 3이므로, 8행 처리에서는 남은 "3개의 요소를 가진 3개의 표본"을 나타내는 (3, 3) shape의 ndarray가 계산되며, 그 값이 위 실행 결과의 두 번째 sample로 표시됩니다. 또한, 3개 표본 요소의 합계가 그 아래의 sum에 올바르게 계산되어 있음을 확인할 수 있습니다.

반복 처리 종료 후의 total에는 실행 결과에 표시되는 두 sum의 요소별 합계가 계산되어 있음을 확인할 수 있으며, 그 아래의 mean에는 total의 각 요소를 표본 크기를 나타내는 size = 8로 나눈 표본 평균이 계산되어 있음을 확인할 수 있습니다.

실행 결과 마지막에는 표본 평균 목록으로부터 np.var를 이용하여 표본 평균의 분산이 계산되어 표시되는 것을 확인할 수 있습니다.

다음으로, 한 번에 추출하는 표본의 수를 제한함으로써, 한 번에 np.random.choice에 의해 계산되는 ndarray의 요소 수 상한이 maxe가 되도록 분할 처리를 하기로 합니다.

구체적으로는, 한 번에 추출하는 표본 수의 최댓값을 나타내는 maxn을 아래 식과 같이 계산합니다.

maxs = min(size, maxe)
maxn = min(num, maxe // maxs)

첫 번째 maxs

는 한 번에 추출할 표본 요소 수의 최댓값을 나타냅니다. 앞서 설명한 처리에서는 s = min(sizeleft, maxe)라는 식으로 계산했지만, sizeleft의 초기값은 size이고 반복 처리를 수행할 때마다 sizeleft의 값은 줄어들기 때문에, min(size, maxe)s의 최댓값이 됩니다.

np.random.choice가 계산하는 2차원 ndarray의 요소 수는 ndarray의 shape이 (s, n)인 경우 s * n이 됩니다. 따라서 s * n의 상한을 maxe로 설정하기 위해서는, s의 최댓값이 maxs인 경우 n의 최댓값을 maxe // maxs 이하로 설정해야 합니다. 또한, 나눗셈의 몫을 나타내는 //로 계산을 수행하는 이유는 n이 정수(integer)여야 하기 때문입니다.

위의 내용을 바탕으로 maxn = min(num, maxe // maxs)라는 계산식으로 한 번에 추출할 표본 수의 상한을 계산함으로써, np.random.choice로 계산하는 2차원 ndarray의 요소 수의 상한이 maxe가 됩니다.

이해가 어려울 수 있으므로 구체적인 예를 들어 설명하겠습니다. 표본 크기 size = 10000, 표본 수 num = 500, 요소의 최댓값 maxe = 1000000(백만)이라고 가정했을 때의 maxsmaxn은 다음과 같이 계산됩니다.

maxs = min(size, maxe) = min(10000, 1000000) = 10000이 된다 -
maxn = min(num, maxe // maxs) = min(500, 1000000 // 10000) = min(500, 100) = 100이 된다

shape을 (size, num)으로 한 2차원 ndarray의 요소 수는 10000 * 500 = 500만이지만, 위로부터 (maxs, maxn) = (10000, 100)의 shape을 가진 ndarray의 요소 수는 상한인 maxe와 동일한 100만이 됩니다.

size = 100, num = 50, maxe = 1000000(백만)인 경우의 maxsmaxn은 다음과 같습니다.

maxs = min(100, 1000000) = 100이 된다 -
maxn = min(50, 1000000 // 100) = min(50, 10000) = 50이 된다

이 경우에는 (size, num)으로 한 2차원 ndarray의 요소 수는 100 * 50 = 5000으로 100만 이하이므로, (maxs, maxn)(size, num)과 완전히 일치합니다.

아래는 그러한 처리를 수행하는 프로그램입니다.

5행: 추출해야 하는 남은 표본 수를 나타내는 변수 numleftnum으로 초기화한다 -
6, 7행: 앞서의 계산식에 따라 maxsmaxn을 계산한다 -
8행: 각 표본의 표본 평균(sample mean)을 기록할 변수를 빈 list로 초기화한다 -
9 ~ 27행: numleft가 0보다 큰 동안 반복 처리를 수행함으로써, 필요한 수의 표본이 추출될 때까지 반복 처리를 수행한다 -
10 ~ 22행: 앞서의 처리와 동일하지만, 11행에서 numleftmaxn의 최솟값을 계산하여 n에 대입하고, 15행의 numn으로 한다는 점이 다르다 -
24행: 각 표본의 표본 평균을 나타내는 list의 요소에, 위에서 계산한 표본 평균 목록을 나타내는 데이터를 + 연산자로 결합하여 추가한다. 아래 노트에서 설명하겠지만, 이때 ndarray의 tolist 메서드를 사용하여 1차원 ndarray를 list로 변환할 필요가 있다 -
25행: 확인을 위해 지금까지 계산한 표본 평균 목록을 표시한다 -
26행: n개의 표본을 추출했으므로 numleft에서 n을 뺀다 -
27행: 다음 표본을 추출함을 명확히 하기 위해 print로 빈 줄을 표시한다 -
28, 29행: np.var

를 사용하여 표본 평균의 분산(variance)을 계산한다. 또한, np.var뿐만 아니라 많은 numpy 함수들은 list를 실제 인자(actual argument)로 기술해도 올바른 계산을 수행할 수 있다.

1 size = 8
2 num = 3
3 maxe = 5
...

행 번호가 없는 프로그램

size = 8
num = 3
maxe = 5
...

실행 결과

sample
[[1]
[0]
...

실행 결과로부터 3개의 표본이 하나씩 순서대로 계산되어 mean의 요소에 추가되어 가는 모습을 확인할 수 있습니다.

아래는 maxe = 20으로 설정했을 경우의 프로그램입니다. 이 경우에는 maxs = min(8, 20) = 8이므로, 한 번의 추출로 표본 크기(sample size)가 8인 표본을 추출합니다.

또한, maxn = min(3, 20 // 8) = min(3, 2) = 2이므로, 한 번의 추출로 최대 2개의 표본을 추출합니다. 실행 결과로부터 실제로 그러한 추출이 이루어지고 있음을 확인해 주세요.

size = 8
num = 3
maxe = 20
...

생략하지 않은 프로그램

size = 8
num = 3
maxe = 20
...

실행 결과

sample
[[0 0]
[1 0]
...

위의 24행에서는 mean에 대입된 list에 대해 +=

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0