히스토그램 밀도 추정 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

# 히스토그램 밀도 추정 문서의 값을 확인한다. 실행: python3 14_histogram-density_verify.py (표준 라이브러리만)
# 자료: 머리카락 길이 10개(cm). 칸은 [시작, 시작 + 폭) 꼴로, 왼쪽 끝은 넣고 오른쪽 끝은 뺀다.
import math
from fractions import Fraction as F

DATA = [3, 4, 5, 5, 6, 7, 12, 13, 14, 20]
N = len(DATA)


def counts(width, origin=0, upto=24):
    """칸마다 들어간 자료 수."""
    out = []
    a = origin
    while a < upto:
        out.append(sum(1 for d in DATA if a <= d < a + width))
        a += width
    return out


def hist_density(x, width, origin=0):
    """x가 든 칸의 높이 = (칸에 든 수 / 전체 수) / 칸 폭."""
    j = math.floor((F(x) - origin) / width)
    a = origin + j * width
    k = sum(1 for d in DATA if a <= d < a + width)
    return F(k, N) / width


def main():
    assert counts(4) == [1, 5, 0, 3, 0, 1]
    assert counts(10, upto=30) == [6, 3, 1]
    c1 = counts(1)
    assert c1.count(0) == 15 and len(c1) == 24 and max(c1) == 2
    print("[OK] 칸 폭 4: 1, 5, 0, 3, 0, 1 / 폭 10: 6, 3, 1 / 폭 1: 24칸 중 15칸이 빈다")

    # 높이 = 도수 / (n × 폭). 칸 넓이의 합은 1
    heights4 = [F(c, N) / 4 for c in counts(4)]
    assert heights4 == [F(1, 40), F(1, 8), 0, F(3, 40), 0, F(1, 40)]
    for w, up in ((1, 24), (4, 24), (10, 30)):
        assert sum(F(c, N) / w * w for c in counts(w, upto=up)) == 1
    print("[OK] 폭 4의 높이 0.025, 0.125, 0, 0.075, 0, 0.025. 어느 폭에서도 넓이의 합은 1")

    # 본문의 네 물음
    assert hist_density(F(26, 5), 4) == F(1, 8)           # 5.2 → 0.125
    assert hist_density(F(79, 10), 4) == F(1, 8)          # 7.9 → 0.125
    assert hist_density(F(81, 10), 4) == 0                # 8.1 → 0
    assert hist_density(F(48, 5), 4) == 0                 # 9.6 → 0
    print("[OK] 폭 4: p(5.2) = 0.125, p(7.9) = 0.125, p(8.1) = 0, p(9.6) = 0")

    # 칸 시작점을 2만큼 밀면 같은 자료, 같은 폭인데 값이 바뀐다
    assert counts(4, origin=-2, upto=22) == [0, 4, 2, 2, 1, 1]
    assert hist_density(F(26, 5), 4, origin=-2) == F(1, 10)   # 0.1
    assert hist_density(F(79, 10), 4, origin=-2) == F(1, 20)  # 0.05
    assert hist_density(F(81, 10), 4, origin=-2) == F(1, 20)
    print("[OK] 시작점 -2: 도수 0, 4, 2, 2, 1, 1 / p(5.2) = 0.1, p(7.9) = p(8.1) = 0.05")

    # 폭에 따른 p(5.2): 1 → 0.2, 4 → 0.125, 10 → 0.06 / p(9.6): 0, 0, 0.06
    assert [hist_density(F(26, 5), w) for w in (1, 4, 10)] == [F(1, 5), F(1, 8), F(3, 50)]
    assert [hist_density(F(48, 5), w) for w in (1, 4, 10)] == [0, 0, F(3, 50)]
    print("[OK] 폭 1·4·10: p(5.2) = 0.2, 0.125, 0.06 / p(9.6) = 0, 0, 0.06")

    # 일반식 p = (k/n)/V: 확률 k/n을 폭 V로 나눈 것. 카드 C2 (자료 200개, 폭 5인 칸에 30개)
    assert F(30, 200) / 5 == F(3, 100)
    # 카드 C3: 폭 2, 시작 0에서 x = 5.2가 든 칸 [4, 6)에는 4, 5, 5 → 3개
    assert hist_density(F(26, 5), 2) == F(3, 20)
    print("[OK] 카드 C2: 0.03, 카드 C3: 0.15")
    print("ALL CHECKS PASSED")


if __name__ == "__main__":
    main()