분산과 표준편차 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

"""분산과 표준편차 검증.

문서: 09.분산과 표준편차 (예시, 정의, 증명, 예제, 활용, 카드 C1~C3)
주장 1: 예시 — 서버 A(늘 100 ms)와 B(50 또는 150 ms 반반): 평균 100으로 같고 분산 0과 2500, 표준편차 0과 50.
주장 2: 정의 — E[(X - μ)²] = E[X²] - μ²(무작위 분포), 주사위 35/12.
주장 3: 성질 — Var(aX + b) = a²Var(X), 독립이면 Var(X + Y) = Var X + Var Y(두 주사위 35/6),
        Var(X + X) = 4 Var X(독립 아님).
주장 4: 예제 — 독립 n개의 평균의 분산은 σ²/n(모의실험, n = 1, 4, 16, 64).
주장 5: 활용 — 10^9 근처 값 네 개(분산 22.5)에서 E[X²] - μ² 공식은 부동소수점 오차로 틀리고, 웰퍼드 방법은 맞다.
주장 6: 카드 C2 — Var X = 2이면 Var(3X + 5) = 18.
"""
from fractions import Fraction
from itertools import product
import random


def var(pmf):
    mu = sum(p * x for x, p in pmf.items())
    return sum(p * (x - mu) ** 2 for x, p in pmf.items())


def main():
    A = {100: Fraction(1)}
    B = {50: Fraction(1, 2), 150: Fraction(1, 2)}
    assert var(A) == 0 and var(B) == 2500
    assert sum(p * x for x, p in B.items()) == 100
    print("[OK] 주장 1: 두 서버")

    rng = random.Random(9)
    for _ in range(200):
        xs = rng.sample(range(-10, 11), 4)
        ps = [Fraction(rng.randint(1, 9)) for _ in xs]
        t = sum(ps)
        pmf = {x: p / t for x, p in zip(xs, ps)}
        mu = sum(p * x for x, p in pmf.items())
        assert var(pmf) == sum(p * x * x for x, p in pmf.items()) - mu * mu
    die = {k: Fraction(1, 6) for k in range(1, 7)}
    assert var(die) == Fraction(35, 12)
    print("[OK] 주장 2: 계산 공식, 주사위 35/12")

    for a, b in ((3, 5), (-2, 7), (0, 4)):
        assert var({a * x + b: p for x, p in die.items()} if a else {b: Fraction(1)}) == a * a * var(die)
    s2 = {}
    for x, y in product(range(1, 7), repeat=2):
        s2[x + y] = s2.get(x + y, 0) + Fraction(1, 36)
    assert var(s2) == Fraction(35, 6) == 2 * var(die)
    assert var({2 * x: p for x, p in die.items()}) == 4 * var(die)
    print("[OK] 주장 3: 성질")

    sigma2 = 35 / 12
    for n in (1, 4, 16, 64):
        means = [sum(rng.randint(1, 6) for _ in range(n)) / n for _ in range(8000)]
        m = sum(means) / len(means)
        v = sum((t - m) ** 2 for t in means) / len(means)
        assert abs(v / (sigma2 / n) - 1) < 0.06
    print("[OK] 주장 4: 평균의 분산 σ²/n")

    data = [1e9 + 4, 1e9 + 7, 1e9 + 13, 1e9 + 16]
    n = len(data)
    naive = sum(x * x for x in data) / n - (sum(data) / n) ** 2
    mean = M2 = 0.0
    for k, x in enumerate(data, 1):
        d = x - mean
        mean += d / k
        M2 += d * (x - mean)
    welford = M2 / n
    assert abs(welford - 22.5) < 1e-6 and abs(naive - 22.5) > 1
    print(f"[OK] 주장 5: 순진한 공식 {naive}, 웰퍼드 {welford}")

    assert 3 * 3 * 2 == 18
    print("[OK] 주장 6: 카드 C2")
    print("ALL CHECKS PASSED")


if __name__ == "__main__":
    main()