확률의 뜻
개념
각 결과가 일어날 가능성이 모두 같은 시행에서, 사건 \(A\)의 수학적 확률은 전체 경우의 수에 대한 사건 \(A\)의 경우의 수의 비율입니다.
\[P(A) = \frac{n(A)}{n(S)}\]
“시행을 늘릴수록 상대도수가 확률에 가까워진다”는 대수의 법칙은 04단원 표본평균의 분포에서 다시 만납니다.
한편 실제로 시행을 \(n\)번 반복했을 때 사건 \(A\)가 일어난 상대도수 \(\dfrac{r_n}{n}\)은 \(n\)이 커질수록 일정한 값에 가까워지는데, 이 값을 통계적 확률이라고 합니다. 동전 앞면처럼 수학적 확률을 아는 시행에서는 상대도수가 바로 그 수학적 확률로 다가갑니다. 아래 앱에서 시행 횟수를 늘려가며 파란 곡선(상대도수)이 점선(수학적 확률)에 달라붙는 모습을 확인해 보세요.
만지며 배우기
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 640
# matplotlib·numpy 없이 stdlib + 인라인 SVG 로만 그린다
import random
from shiny import App, reactive, render, ui
ACCENT = "#2563eb"
INK = "#111827"
MUTED = "#6b7280"
GRID = "#e5e7eb"
TRIALS = {
"coin": {"name": "동전 던지기 — 앞면", "p": 0.5, "label": "1/2"},
"die": {"name": "주사위 던지기 — 6의 눈", "p": 1 / 6, "label": "1/6"},
}
def running_mean(hits):
out = []
s = 0
for i, h in enumerate(hits):
s += h
out.append(s / (i + 1))
return out
def line_svg(run, p):
n = len(run)
W, H, ml, mr, mt, mb = 470, 300, 40, 12, 14, 32
pw, ph = W - ml - mr, H - mt - mb
def X(i):
return ml + (i / max(1, n - 1)) * pw
def Y(v):
return mt + ph - v * ph
grid = ""
for v in (0.0, 0.25, 0.5, 0.75, 1.0):
yy = Y(v)
grid += (f'<line x1="{ml}" y1="{yy:.1f}" x2="{W-mr}" y2="{yy:.1f}" stroke="{GRID}"/>'
f'<text x="{ml-6}" y="{yy+3:.1f}" font-size="9" fill="{MUTED}" '
f'text-anchor="end">{v:.2f}</text>')
step = max(1, n // 400)
idx = list(range(0, n, step))
if idx[-1] != n - 1:
idx.append(n - 1)
path = "M" + " L".join(f"{X(i):.1f},{Y(run[i]):.1f}" for i in idx)
pline = (f'<line x1="{ml}" y1="{Y(p):.1f}" x2="{W-mr}" y2="{Y(p):.1f}" '
f'stroke="{INK}" stroke-width="1" stroke-dasharray="5 3"/>')
return (
f'<svg viewBox="0 0 {W} {H}" style="width:100%;height:auto;'
'font-family:-apple-system,sans-serif;">'
f'{grid}{pline}<path d="{path}" fill="none" stroke="{ACCENT}" stroke-width="1.4"/>'
f'<text x="{ml+pw/2:.0f}" y="{H-4}" font-size="10" fill="{MUTED}" '
'text-anchor="middle">시행 횟수</text>'
f'<text x="6" y="{mt-2}" font-size="10" fill="{MUTED}">상대도수</text>'
'</svg>'
)
app_ui = ui.page_sidebar(
ui.sidebar(
ui.input_select(
"trial", "시행 종류",
{k: v["name"] for k, v in TRIALS.items()},
),
ui.input_slider("n", "시행 횟수 n", min=10, max=10000, value=200, step=10),
ui.input_action_button("redraw", "다시 시행"),
width=240,
),
ui.card(
ui.card_header("상대도수의 변화(파랑) vs 수학적 확률(점선)"),
ui.output_ui("freq_plot"),
),
ui.card(ui.output_ui("readout")),
fillable=True,
)
def server(input, output, session):
@reactive.calc
def draws():
input.redraw() # 버튼을 누르면 새로 시행
p = TRIALS[input.trial()]["p"]
return [1 if random.random() < p else 0 for _ in range(input.n())]
@render.ui
def freq_plot():
p = TRIALS[input.trial()]["p"]
return ui.HTML(line_svg(running_mean(draws()), p))
@render.ui
def readout():
sc = TRIALS[input.trial()]
hits = draws()
n = len(hits)
rel = sum(hits) / n
return ui.HTML(
f'<p class="hs-readline">'
f"{n:,}번 시행했을 때 상대도수는 {rel:.4f}, "
f"수학적 확률은 {sc['label']} = {sc['p']:.4f}이고 "
f"차이는 {abs(rel - sc['p']):.4f}입니다. "
f"시행을 늘릴수록 상대도수가 수학적 확률에 가까워지는데, "
f"이렇게 정해지는 값이 통계적 확률입니다.</p>"
)
app = App(app_ui, server)
앱의 핵심 계산은 이 몇 줄이 전부입니다.
import random
p = 1 / 6 # 주사위 6의 눈의 수학적 확률
n = 10000
hits = [1 if random.random() < p else 0 for _ in range(n)]
run, s = [], 0 # 시점별 상대도수
for i, h in enumerate(hits):
s += h
run.append(s / (i + 1))
print(run[9], run[99], run[-1], p) # n이 커질수록 p에 접근스스로 확인
힌트문제 1 — n = 10일 때와 n = 10000일 때 ’다시 시행’을 여러 번 눌러 보라. 곡선의 끝값은 어느 쪽이 더 안정적인가?
\(n = 10\)일 때는 누를 때마다 끝값이 0.1 단위로 크게 출렁이지만, \(n = 10000\)일 때는 거의 매번 수학적 확률 바로 근처에서 끝납니다. 상대도수는 시행 횟수가 충분히 커야 믿을 수 있는 값이 됩니다.
힌트문제 2 — 윷가락처럼 앞뒤 모양이 다른 물체는 수학적 확률을 구하기 어렵다. 이럴 때 확률은 어떻게 정하는가?
각 결과의 가능성이 같다고 볼 수 없으므로 경우의 수의 비로는 구할 수 없습니다. 대신 실제로 많이 던져 보고 상대도수가 다가가는 값, 즉 통계적 확률로 정합니다. 의약품의 치료 효과, 야구 타율처럼 현실의 많은 확률이 이 방식으로 정해집니다.