난수 생성 원리: 컴퓨터가 뽑은 랜덤 숫자는 정말 공정할까

난수 생성 원리: 컴퓨터가 뽑은 랜덤 숫자는 정말 공정할까

회식 경품을 번호표로 추첨하거나 수업 시간에 발표자를 고를 때, 요즘은 대부분 휴대폰이나 웹사이트의 랜덤 숫자 뽑기를 씁니다. 그런데 결과가 나오면 꼭 이런 말이 따라붙습니다. "그거 진짜 랜덤 맞아? 컴퓨터는 정해진 대로만 움직인다던데."

그 의심은 절반은 맞고 절반은 틀립니다. 컴퓨터는 계산 기계라서 같은 입력을 넣으면 같은 결과를 냅니다. 그래서 대부분의 컴퓨터 난수는 사실 계산으로 만든 '무작위처럼 보이는 수'입니다. 하지만 어떤 방식으로 만들었느냐에 따라 추첨에 써도 전혀 문제가 없는 난수가 있고, 쉽게 예측될 수 있는 난수도 있습니다. 이 글에서는 난수 생성 원리를 일반인 눈높이로 풀고, 대표 알고리즘과 숨은 함정인 모듈로 편향, 그리고 "이 추첨 도구는 공정한가"를 판단하는 기준까지 한 번에 정리합니다.


1. 컴퓨터 난수는 세 종류 — 무엇으로 만들었는지가 공정성을 가른다

난수(亂數)의 뜻은 '규칙 없이 나오는 수', 영어로 random number입니다. 핵심 조건은 두 가지입니다. 첫째, 모든 값이 같은 확률로 나와야 합니다(균등성). 둘째, 앞서 나온 값을 보고 다음 값을 맞힐 수 없어야 합니다(예측 불가능성). 컴퓨터가 만드는 난수는 이 두 조건을 얼마나 충족하느냐에 따라 세 갈래로 나뉩니다.

구분 만드는 방법 예측 가능성 재현 가능 대표 예 어울리는 용도
의사난수(PRNG) 시드 값에 수식을 반복 적용 내부 상태를 알면 예측 가능 같은 시드면 같은 수열 Math.random, 파이썬 random 게임, 시뮬레이션, 단순 셔플
암호학적 난수(CSPRNG) 운영체제가 모은 엔트로피 + 암호 알고리즘 현실적으로 예측 불가 불가 crypto.getRandomValues, 파이썬 secrets 추첨, 비밀번호, 보안 토큰
진짜 난수(TRNG) 열잡음·대기 잡음·방사성 붕괴 같은 물리 현상 측정 원리적으로 예측 불가 불가 하드웨어 난수 칩, 대기 잡음 기반 서비스 암호 장비의 씨앗, 복권 추첨기

일상적인 추첨에서 가장 중요한 칸은 '예측 가능성'입니다. 결과가 고르게 나오더라도 누군가 다음 숫자를 맞힐 수 있다면 공정한 추첨이라고 하기 어렵기 때문입니다.

2. 의사난수의 원리 — 시드 하나에서 수열 전체가 결정된다

의사난수 생성기(PRNG)는 시드(seed)라는 출발값을 받아 정해진 수식을 계속 적용합니다. 직전 결과가 다음 계산의 입력이 되므로, 시드만 같으면 몇 번을 돌려도 똑같은 수열이 나옵니다. 마인크래프트에서 같은 시드를 넣으면 같은 지형이 생기는 것도 이 성질 덕분입니다. 반대로 시드를 현재 시각 같은 값으로 바꿔 주면 매번 다른 수열처럼 보입니다.

가장 오래되고 단순한 방식이 선형 합동 생성기(LCG)입니다. 수식은 다음과 같습니다.

다음 값 = (a × 현재 값 + c) mod m

이해를 돕기 위해 아주 작은 값으로 돌려 보겠습니다. a=5, c=3, m=16, 시드=7로 두면 결과는 이렇습니다.

순서 계산 결과
1 (5×7+3) mod 16 = 38 mod 16 6
2 (5×6+3) mod 16 = 33 mod 16 1
3 (5×1+3) mod 16 = 8 mod 16 8
4 (5×8+3) mod 16 = 43 mod 16 11
5 (5×11+3) mod 16 = 58 mod 16 10
6 (5×10+3) mod 16 = 53 mod 16 5

이어서 12, 15, 14, 9, 0, 3, 2, 13, 4가 나오고 열여섯 번째에 다시 7로 돌아옵니다. 0부터 15까지 모든 수가 한 번씩 나오니 언뜻 무작위처럼 보입니다. 하지만 자세히 보면 홀수와 짝수가 7(홀), 6(짝), 1(홀), 8(짝)처럼 번갈아 나옵니다. m이 2의 거듭제곱인 LCG는 낮은 자리 비트일수록 주기가 짧아지는 약점이 있는데, 이 예시가 바로 그 모습입니다. 실제 LCG는 m을 2³¹이나 2⁶⁴처럼 크게 잡아 주기를 늘리지만, 수식 구조가 단순한 만큼 연속된 출력 몇 개로 다음 값을 계산해 낼 수 있습니다.

3. 대표 난수 생성 알고리즘 3가지 — LCG·메르센 트위스터·xorshift

난수 생성 알고리즘 종류는 많지만, 일반 사용자가 마주치는 것은 대부분 아래 세 가지 계열입니다.

알고리즘 등장 주기 장점 약점 쓰이는 곳
LCG(선형 합동) 1949년 최대 m (보통 2³¹~2⁶⁴) 수식 한 줄, 매우 빠름 낮은 비트 패턴, 쉽게 예측 옛 C 언어 rand, 간단한 임베디드
메르센 트위스터(MT19937) 1997년 2¹⁹⁹³⁷−1 주기가 사실상 무한, 통계 품질 우수 연속 출력 624개로 내부 상태 복원 가능 파이썬 random, PHP mt_rand, 엑셀 RAND(2010 이후)
xorshift 계열 2003년 2¹²⁸−1 (xorshift128+) 비트 이동·XOR만 써서 빠르고 가벼움 엄격한 통계 검정 일부 미통과, 예측 가능 주요 브라우저의 Math.random

메르센 트위스터는 32비트 정수 624개로 된 내부 상태를 가지고 있습니다. 출력값을 연속으로 624개 관찰하면 이 상태를 거꾸로 계산해 낼 수 있고, 그다음부터 나올 숫자를 전부 맞힐 수 있습니다. 파이썬 공식 문서가 random 모듈을 보안 목적에 쓰지 말고 secrets 모듈을 쓰라고 명시하는 이유가 여기에 있습니다.

브라우저의 Math.random도 사정이 비슷합니다. 크롬의 V8 엔진은 2015년부터 xorshift128+를 쓰고 있는데, 통계적으로는 충분히 고르게 나오지만 내부 상태가 128비트에 불과해 출력 몇 개로 상태를 추정하는 공격이 알려져 있습니다. 즉 의사난수의 문제는 '고르지 않다'가 아니라 '맞힐 수 있다'입니다.

4. 암호학적 난수와 진짜 난수 — 예측 불가능성은 어디서 오나

4-1. 진짜 난수: 물리 세계의 잡음을 잰다

진짜 난수 생성기(TRNG)는 계산이 아니라 측정으로 숫자를 얻습니다. 전자 회로의 열잡음, 대기 중 전파 잡음, 방사성 원소의 붕괴 시점처럼 물리 법칙상 미리 알 수 없는 현상을 숫자로 바꿉니다. 클라우드플레어가 사무실 벽에 용암 램프 수십 개를 세워 두고 그 영상을 난수의 씨앗으로 쓰는 것도 같은 발상입니다. 다만 물리 측정은 느리고, 측정값이 한쪽으로 쏠리지 않도록 보정 과정이 필요해서 대량의 난수를 바로 공급하기에는 불편합니다.

4-2. 암호학적 난수: 진짜 난수를 씨앗으로 계산을 돌린다

그래서 실제로는 두 방식을 섞습니다. 운영체제가 키보드 입력 간격, 디스크와 네트워크 인터럽트 타이밍, CPU의 하드웨어 난수 명령 같은 예측하기 어려운 값(엔트로피)을 꾸준히 모으고, 이를 씨앗으로 ChaCha20 같은 암호 알고리즘을 돌려 빠르게 난수를 뽑아냅니다. 이것이 암호학적 난수 생성기(CSPRNG)입니다. 출력을 아무리 많이 모아도 내부 상태나 다음 값을 계산해 낼 수 없도록 설계되어 있다는 점이 일반 PRNG와의 결정적 차이입니다.

웹 브라우저에서는 이 차이가 함수 이름으로 드러납니다.

항목 Math.random() crypto.getRandomValues()
생성 방식 엔진 내장 PRNG(xorshift128+ 등) 운영체제 CSPRNG
결과 형태 0 이상 1 미만 실수 정수 배열(Uint8·Uint32 등)
예측 가능성 출력 여러 개로 추정 가능 현실적으로 불가
표준 문서의 권고 보안 용도에 쓰지 말 것 보안·추첨 용도에 적합
적합한 곳 애니메이션, 게임 연출 추첨, 비밀번호, 인증 토큰

경품 추첨처럼 결과에 이해관계가 걸린 일이라면, 그 도구가 어느 쪽을 쓰는지가 첫 번째 확인 포인트입니다.

5. 모듈로 편향 — 1~6을 나머지 연산으로 뽑으면 생기는 일

좋은 난수 원천을 써도 마지막 단계에서 공정성이 깨질 수 있습니다. 대표적인 함정이 모듈로 편향(modulo bias)입니다. 난수 생성기는 보통 0~255(1바이트)나 0~약 43억(32비트) 같은 정해진 범위의 정수를 줍니다. 이걸 주사위 1~6으로 바꾸려고 "6으로 나눈 나머지 + 1"을 하는 경우가 많은데, 256은 6으로 나누어떨어지지 않습니다.

256 = 6 × 42 + 4이므로 나머지 0~3은 43번씩, 나머지 4~5는 42번씩 나옵니다. 주사위 눈으로 바꾸면 다음과 같습니다.

원천 범위 뽑을 범위 자주 나오는 쪽 확률 덜 나오는 쪽 확률 차이
0~255 (1바이트) 1~6 1~4: 각 16.80% (43/256) 5~6: 각 16.41% (42/256) 약 1.02배
0~255 (1바이트) 1~100 1~56: 각 1.17% (3/256) 57~100: 각 0.78% (2/256) 1.5배
0~2³²−1 (32비트) 1~6 약 7억 1,600만 번에 1번꼴 차이 — 사실상 무시 가능

두 번째 줄을 보면 심각성이 보입니다. 1바이트 난수로 1~100번 참가자 중 당첨자를 뽑으면 1~56번이 57~100번보다 1.5배 잘 뽑힙니다. 원천 범위를 32비트로 키우면 편향이 아주 작아지지만, 완전히 없어지지는 않습니다.

이 문제를 깔끔하게 없애는 방법이 거절 샘플링(rejection sampling)입니다. 원천 범위에서 뽑을 범위의 배수가 되는 구간까지만 쓰고, 그 밖의 값이 나오면 버리고 다시 뽑습니다. 1바이트로 1~6을 뽑는다면 252(=6×42) 이상인 252~255가 나왔을 때만 다시 뽑으면 됩니다. 다시 뽑을 확률은 4/256, 약 1.6%라 속도 손해도 거의 없습니다.

// 0 이상 n 미만의 균등한 정수 (모듈로 편향 제거)
function randInt(n) {
  const limit = Math.floor(2 ** 32 / n) * n
  const a = new Uint32Array(1)
  do crypto.getRandomValues(a)
  while (a[0] >= limit)
  return a[0] % n
}

여러 명 중 중복 없이 순서를 정할 때는 이렇게 만든 균등 정수로 피셔-예이츠(Fisher–Yates) 셔플을 하면 모든 순서가 같은 확률로 나옵니다. 배열을 Math.random 기준으로 정렬하는 흔한 꼼수는 정렬 알고리즘에 따라 특정 순서가 더 자주 나오는 문제가 있어 피하는 편이 좋습니다.

6. 그래서 추첨에 써도 공정한가 — 용도별 판단 기준

모든 상황에 암호학적 난수가 필요한 것은 아닙니다. 결과에 무엇이 걸려 있는지에 따라 기준을 정하면 됩니다.

용도 필요한 수준 확인할 점
게임 연출, 화면 효과, 시뮬레이션 PRNG로 충분 재현이 필요하면 시드를 기록
친구끼리 메뉴·순서 정하기 PRNG도 무방 결과를 다 같이 보는 화면에서 실행
수업 발표자·청소 당번 CSPRNG 권장 번호 범위를 미리 공개
경품·사은품 추첨 CSPRNG + 편향 제거 필수 중복 금지 설정, 추첨 화면 녹화
비밀번호·인증 코드 CSPRNG 필수 길이와 문자 종류로 엔트로피 확보
법정 복권·공식 추첨 인증된 물리 추첨기 + 입회 외부 감사, 절차 공개

여기에 절차적 공정성도 더해야 합니다. 아무리 좋은 알고리즘이어도 원하는 결과가 나올 때까지 다시 누를 수 있다면 의미가 없습니다. 실무에서는 다음 다섯 가지를 체크하면 대부분의 시비를 막을 수 있습니다.

  • 난수 원천이 암호학적 난수(crypto.getRandomValues, secrets 등)인가
  • 범위를 바꾸는 단계에서 모듈로 편향을 제거했는가
  • 참가자 번호와 범위, 당첨 인원을 추첨 전에 공개했는가
  • 다시 뽑기 규칙(불참자 발생 시 등)을 미리 정했는가
  • 추첨 장면을 화면 공유나 녹화로 남겼는가

7. 자주 묻는 질문 (FAQ)

Q1. 컴퓨터는 진짜 난수를 못 만드나요?

일반 계산만으로는 만들 수 없습니다. 계산은 같은 입력에 같은 출력을 내기 때문입니다. 다만 요즘 컴퓨터는 운영체제가 하드웨어 잡음이나 입력 타이밍 같은 물리적 불확실성을 모아 씨앗으로 쓰므로, 그 위에서 동작하는 암호학적 난수는 실용적으로 진짜 난수와 구별할 수 없는 수준입니다.

Q2. 같은 숫자가 연달아 나오면 조작된 건가요?

아닙니다. 1~10에서 하나를 뽑을 때 직전과 같은 숫자가 나올 확률은 매번 10%입니다. 오히려 같은 숫자가 한 번도 겹치지 않고 골고루만 나온다면 그쪽이 덜 무작위적입니다. 사람은 무작위를 '골고루 섞인 것'으로 착각하는 경향이 있어 연속 결과를 의심하게 됩니다.

Q3. 난수표는 무엇이고 지금도 쓰이나요?

난수표는 무작위 숫자를 미리 표로 인쇄해 둔 것입니다. 1955년 미국 랜드연구소가 펴낸 100만 개 난수 책이 유명하며, 통계 표본 추출이나 암호 작성에 쓰였습니다. 지금은 컴퓨터 난수생성기가 그 역할을 대신하지만, 통계 교육이나 오프라인 표본 추출에서는 여전히 볼 수 있습니다.

Q4. 엑셀 RAND 함수로 경품 추첨을 해도 되나요?

친목 모임 정도라면 큰 문제는 없습니다. 다만 엑셀의 RAND는 의사난수이고, 시트를 다시 계산할 때마다 값이 바뀌어 결과를 확정하기 번거롭습니다. 당첨자를 두고 이견이 생길 수 있는 추첨이라면 암호학적 난수와 편향 제거를 쓰는 도구에서 화면을 공유하며 뽑는 편이 깔끔합니다.

8. 난수생성기로 경품·발표자를 공정하게 추첨하기

핸디웹의 랜덤 숫자 생성기는 이 글에서 다룬 공정성 기준을 그대로 따릅니다. 숫자를 브라우저의 crypto.getRandomValues로 뽑고, 앞의 코드처럼 거절 샘플링으로 모듈로 편향을 없애 범위 안의 모든 숫자가 같은 확률로 나옵니다. 참가자 수만큼 범위를 잡고 당첨 인원을 개수로 넣은 뒤 중복을 끄면, 번호표 경품 추첨이 한 번에 끝납니다. 불참자 번호는 제외 칸에 적어 두면 됩니다.

여러 사람이 각자 하나씩 골라 당첨·꽝을 가리고 싶다면 제비뽑기가 편합니다. 2~30명 분의 카드를 같은 방식의 균등 난수와 피셔-예이츠 셔플로 섞어 깔아 주므로, 누가 어떤 카드를 고르든 당첨 확률은 같습니다. 로또처럼 1~45에서 6개를 중복 없이 고르는 경우는 로또 번호 생성기를 쓰면 됩니다. 세 도구 모두 계산이 기기 안에서만 이뤄지고 서버로 보내지 않습니다. 난수가 보안에서 어떻게 쓰이는지 더 알고 싶다면 비밀번호 강도 측정 알고리즘과 보안 토큰 생성 표준도 함께 읽어 보세요.

함께 보면 좋은 글

목록으로 돌아가기