한양과학기술고등학교

에듀테크 프로젝트 대시보드

의사난수 생성기 (PRNG)

의사난수 생성기 (PRNG)

컴퓨터는 사실 진짜 무작위를 만들지 못한다. random() 이 뱉는 값은 완전히 정해진 계산식이 만든 수열일 뿐이다 — 그래서 의사(pseudo) 난수다. 가장 오래된 방법인 선형 합동법(LCG)은 직전 값에 곱하고 더하고 나머지를 취하는 딱 한 줄이다. 같은 시드(seed)로 출발하면 항상 똑같은 수열이 나오고(게임 리플레이· 테스트 재현의 핵심), 값이 유한하므로 언젠가 반드시 반복된다(주기). 게다가 상수를 잘못 고르면 난수쌍을 찍었을 때 격자 무늬(초평면)가 드러나 전혀 무작위로 보이지 않는다.

xn+1  =  ( a · xn  +  c )  mod  m   ( 0 ~ m−1 의 정수, 필요하면 xn/m 로 0~1 실수화 )

LCG 한 걸음씩 — 곱하고, 더하고, 나머지

걸음 n
0
현재 xₙ
1
a·xₙ + c
—
xₙ / m
0.0625
관측 주기
—
수열: 1
한 걸음 을 눌러 보세요. 다음 값은 (a·xₙ + c) mod m 하나로 정해집니다. 이미 나온 값이 다시 등장하는 순간, 그 뒤로는 같은 패턴이 영원히 반복됩니다 (그게 주기).

난수쌍 산점도 — 좋은 난수 vs 격자 무늬

연속으로 나온 두 값을 좌표 (xₙ, xₙ₊₁) 로 찍어 점을 수천 개 뿌린다. 난수라면 정사각형을 고르게 덮어야 한다. 그런데 곱수 a 를 잘못 고르면 점들이 몇 개의 직선(고차원에서는 초평면) 위에만 놓인다 — 이것이 모든 LCG의 약점인 격자 구조(Marsaglia, 1968)다. RANDU 프리셋은 3차원으로 보면 점이 딱 15개 평면에 갇힌다.

가로 = xₙ, 세로 = xₙ₊₁  ·  점 3000개
위 막대는 출력값을 20개 구간으로 나눈 도수분포(히스토그램)다. 1차원 균일성은 나쁜 LCG도 대체로 통과한다 — 격자 무늬는 2차원 이상에서만 드러난다. 그래서 좋은 난수인지 보려면 쌍·삼중쌍을 봐야 한다.
a
16807
m
2³¹−1
판정
—

시드 = 재현성 — 같은 씨앗, 같은 수열

PRNG는 시드만 같으면 몇 번을 다시 돌려도 완전히 동일한 수열을 만든다. 게임의 "시드 입력" 맵 생성, 유닛 테스트의 seed(42), 과학 실험의 재현이 모두 이 성질 덕분이다. 아래 두 실행의 시드를 같게 두면 수열이 포개지고, 다르게 두면 완전히 갈라진다.

두 시드가 같으므로 파란 선과 주황 점이 정확히 겹칩니다 — 결정론적입니다.

코드로 보기 — LCG와 Xorshift

# 1) 선형 합동법 (LCG) — glibc/자바 계열이 오래 쓴 방식
class LCG:
    def __init__(self, seed):
        self.state = seed
        self.a, self.c, self.m = 6364136223846793005, 1442695040888963407, 2**64
    def next_u01(self):
        self.state = (self.a * self.state + self.c) % self.m   # ★ 딱 한 줄
        return self.state / self.m                             # 0.0 ~ 1.0

# 2) Xorshift — 시프트와 XOR만으로 훨씬 나은 통계 품질
def xorshift32(x):
    x ^= (x << 13) & 0xFFFFFFFF
    x ^= (x >> 17)
    x ^= (x << 5)  & 0xFFFFFFFF
    return x                    # 주기 2³²−1, 격자 무늬 거의 없음

# 3) 재현성: 시드를 고정하면 언제 돌려도 같은 수열
import random
random.seed(42); print([random.randint(1,6) for _ in range(5)])  # [6, 1, 1, 6, 3]
random.seed(42); print([random.randint(1,6) for _ in range(5)])  # [6, 1, 1, 6, 3]  ← 똑같음
  • 좋은 LCG의 조건 (Hull–Dobell 정리): 전체 주기 m 을 얻으려면 ① c 와 m 이 서로소, ② m 의 모든 소인수가 a−1 을 나눔, ③ m 이 4의 배수면 a−1 도 4의 배수.
  • 하위 비트를 믿지 마라: m = 2ᵏ LCG는 최하위 비트의 주기가 2밖에 안 된다(0,1,0,1…). rand() % 2 로 동전 던지기 하면 안 되는 이유.
  • 암호용으로 절대 금지: 출력 몇 개만 보면 a, c 를 역산해 미래 전체를 예측할 수 있다. 보안에는 /dev/urandom·secrets 같은 CSPRNG를 쓴다.
  • 오늘날 표준: 파이썬·NumPy는 Mersenne Twister(주기 2¹⁹⁹³⁷−1) 또는 PCG, 게임 엔진은 xoshiro/xorshift 계열을 쓴다.

PRNG vs TRNG(진짜 난수) vs CSPRNG

PRNG (LCG·MT·xorshift)CSPRNG (암호용)TRNG (하드웨어)
근원결정론적 수식 + 시드수식 + 예측 불가 엔트로피물리 잡음(열·양자)
재현 가능예 (시드만 있으면)아니오아니오
속도매우 빠름빠름느림
예측 가능성출력 보면 역산 가능계산상 불가능불가능
용도게임·시뮬레이션·셔플·테스트키·토큰·비밀번호 솔트CSPRNG 시드·복권 추첨
  • 대부분의 프로그램은 PRNG로 충분하다 — 빠르고, 버그 재현이 되니까. "비밀"이 걸리는 순간에만 CSPRNG로 바꾼다.
  • 위 산점도에서 RANDU 프리셋을 골라 보라. 1차원 히스토그램은 멀쩡한데 2차원에서 대각 줄무늬가 보인다 — 30년간 과학 계산을 오염시킨 실제 사례다.