투명성

이 돌림판, 정말 랜덤인가요?

네. 결과는 애니메이션이 시작되기 전에 브라우저의 암호학적 난수 생성기로 이미 정해집니다. 서버가 결과에 손대지 않고, 직접 확률을 설정하지 않는 한 모든 칸의 확률은 똑같습니다.

실제로 동작하는 코드
const MAX = 2 ** 32

function randomInt(n) {
  // 나누어떨어지지 않는 끝부분을 버려 n개 결과의 확률을 완전히 같게
  const limit = MAX - (MAX % n)
  let value
  do {
    value = crypto.getRandomValues(new Uint32Array(1))[0]
  } while (value >= limit)
  // 이제 나머지 연산에 편향이 없음
  return value % n
}

사이트 공용 난수 모듈의 randomInt 함수입니다. 브라우저 개발자 도구의 소스 패널에서 전체 코드를 볼 수 있습니다.

직접 해 보기: 10,000번 모의 회전

같은 크기의 칸 8개. 공정한 돌림판이라면 칸마다 약 1,250번씩 당첨되고 자연스러운 작은 오차가 생깁니다.

횟수
칸 수
확률

점선: 완벽히 공정한 돌림판에서 그 칸의 기대 횟수

기대값과의 최대 차이–
카이제곱 값, 자유도 7–14.07 미만이면 유의수준 5%에서 통과
판정–

균등 모드는 왼쪽에 보이는 randomInt 함수를 회전마다 한 번씩 호출하며, 모든 계산은 브라우저 안에서 이루어집니다. 카이제곱 검정은 각 막대가 기대값에서 얼마나 떨어져 있는지를 합한 값입니다. 공정한 난수라도 약 20번에 1번은 5% 기준을 넘기 때문에 한 번 불통과는 자연스러운 일이고, 매번 불통과라면 그때 문제가 있는 것입니다.

  • 이벤트·라이브 추첨추첨 전에 이 페이지를 공유하면 당첨자가 어떻게 뽑히는지 보여 줄 수 있어 조작 의심이 줄어듭니다.
  • 수업 시간 발표자 뽑기선생님도 결과를 바꿀 수 없다는 걸 알면 뽑힌 학생도 납득하기 쉽습니다.
  • 단톡방 내기와 결정점심 메뉴, 청소 당번, 커피 쏘기. 누가 조작이라고 하면 이 페이지를 보내 주세요.
  • 확률 설정 확인30% 칸이 장기적으로 10번 중 3번쯤 당첨되는 이유와 짧게 보면 들쭉날쭉한 이유를 알 수 있습니다.
사용 방법

돌리기를 누르면 일어나는 일

  1. 돌리기를 누릅니다

    페이지가 브라우저의 crypto.getRandomValues에 새 32비트 난수를 요청합니다. 운영체제의 안전한 난수원에서 만들어지는 값입니다.

  2. 멈출 위치가 바로 정해집니다

    난수로 몇 바퀴 돌지와 최종 각도가 결정됩니다. 이 순간 결과는 이미 확정이고, 멈췄을 때 바늘 아래에 있는 칸이 당첨입니다.

  3. 그다음 애니메이션

    회전, 감속, 똑딱 소리는 연출일 뿐입니다. 오래 지켜보거나 빠른 기기에서 돌려도 이미 정해진 멈춤 각도는 바뀌지 않습니다.

  4. 데이터는 기기 밖으로 나가지 않습니다

    추첨은 브라우저에서 이루어지고, 결과를 정하려고 목록을 서버로 보내지 않습니다. 뒤에서 조작할 스위치가 없습니다.

이런 점이 좋아요

공정한 추첨을 지키는 여섯 가지 장치

암호학적 난수crypto.getRandomValues는 보안 키 생성에 쓰이는 난수원으로, 예측 가능한 Math.random과 다릅니다.
나머지 편향 없음거부 샘플링으로 작은 숫자 쪽에 치우치게 만드는 값을 버려, n개 결과가 정확히 n분의 1이 됩니다.
Fisher–Yates 셔플목록 섞기, 팀 나누기, 순서 정하기는 Fisher–Yates 방식이라 모든 순서가 같은 확률입니다.
연출 전에 결정누르는 순간 멈춤 각도가 정해집니다. 속도 설정은 지켜보는 시간만 바꿉니다.
서버가 관여하지 않음결과는 내 기기에서 계산됩니다. 계정도, 숨은 대기열도, 원격 조작도 없습니다.
가중치는 직접 넣을 때만확률을 설정하면 칸 크기가 달라져, 눈에 보이는 면적이 곧 실제 확률이 됩니다.

단순 나머지 연산이 살짝 불공정한 이유와 거부 샘플링의 해결법

난수 생성기는 정해진 범위의 정수를 돌려줍니다. 브라우저는 32비트 정수, 즉 4,294,967,296가지 값을 줍니다. n개 중 하나를 고르는 가장 쉬운 방법은 value % n, 곧 n으로 나눈 나머지를 쓰는 것입니다. 그런데 2의 32제곱이 n으로 나누어떨어지지 않으면 몇몇 나머지가 한 번씩 더 나옵니다.

0부터 7까지만 나오는 작은 생성기로 3개 중 하나를 고르는 예를 보면 쉽게 이해됩니다.

방식항목 1항목 2항목 3
단순 나머지, 값 0부터 70, 3, 6: 8분의 31, 4, 7: 8분의 32, 5: 8분의 2뿐
거부 샘플링, 6과 7은 버리고 다시 뽑기0, 3: 6분의 21, 4: 6분의 22, 5: 6분의 2

이 사이트의 randomInt는 2의 32제곱에서 나머지를 뺀 limit을 계산하고, limit 이상인 값이 나오면 버리고 다시 뽑습니다. 32비트에서는 원래 편향도 아주 작아서 한 반 인원 규모의 명단이라면 1억분의 1 미만이고 다시 뽑는 일도 거의 없지만, 결과는 거의 정확한 수준이 아니라 완전히 정확해집니다.

돌림판은 난수를 어떻게 당첨 결과로 바꾸나

돌림판은 몰래 번호를 고른 뒤 도는 척하는 방식이 아닙니다. 실제 멈춤 각도를 난수로 정합니다. 5바퀴에서 8바퀴 사이의 무작위 회전 수와 무작위 최종 각도, 둘 다 crypto.getRandomValues에서 나옵니다. 각 칸은 원의 일부를 차지하므로 바늘 아래에서 멈출 확률은 그 칸이 360도에서 차지하는 비율과 정확히 같고, 오차는 약 40억분의 1입니다.

항목에 확률을 퍼센트로 넣으면 그 비율대로 칸이 다시 그려지고, 합계가 100이 되어야 돌릴 수 있습니다. 50% 칸은 원의 절반이라 장기적으로 절반쯤 당첨됩니다. 돌림판 없이 목록에서 고르는 도구는 weightedIndex를 써서 전체 가중치 위에 무작위 지점을 찍고, 그 지점이 들어가는 항목을 고릅니다.

  • 돌리기 전에 돌림판이 어디에 멈춰 있었는지는 상관없습니다. 출발점과 관계없이 최종 각도는 균등하게 분포합니다.
  • 회전 속도와 시간은 애니메이션만 늘립니다. 멈춤 각도는 첫 프레임 전에 정해집니다.
  • 당첨자를 제외하면 돌림판이 다시 그려지고, 남은 항목끼리 원을 나눠 가집니다.

같은 사람이 연속으로 뽑히는 건 정상: 진짜 랜덤의 모습

랜덤 뽑기에 대한 가장 흔한 불만은 또 저 사람이냐는 것입니다. 공정한 난수에는 기억이 없어서 연속 당첨은 가능할 뿐 아니라 당연히 일어납니다. 다음 회차가 직전 결과와 같을 확률은 단순히 1을 항목 수로 나눈 값입니다.

돌림판 항목 수두 번 연속 같은 결과20번 돌릴 때 연속 당첨이 한 번이라도 나올 확률
250%거의 확실
425%약 99.6%
812.5%약 92%
128.3%약 81%
303.3%약 47.5%

모두 한 번씩 뽑힌 뒤에야 중복되길 원한다면, 그건 더 공정한 추첨이 아니라 비복원 추출입니다. 이름 뽑기 돌림판에서 제외하고 다시 돌리기를 쓰거나, 목록 섞기 도구로 명단 전체를 한 번에 섞으세요.

Math.random을 쓰는 곳, 그리고 결과에 영향이 없는 이유

Math.random은 꾸미기 효과에는 충분하기 때문에 몇몇 애니메이션에서 씁니다. 주사위의 살짝 기운 모습, 구슬 레이스의 흔들림, 숫자가 정해지기 전 빠르게 바뀌는 표시 등입니다. 이때도 결과 자체는 이미 암호학적 난수로 정해져 있고, 효과는 화면에 보이는 순서를 바꾸지 않을 만큼 작게 유지합니다.

공용 난수 모듈은 브라우저에 Web Crypto가 아예 없을 때만 Math.random으로 대체하며, 요즘은 아주 오래된 브라우저에서나 해당됩니다. 현재 버전의 Chrome, Safari, Firefox, Edge, 삼성 인터넷, 카카오톡 인앱 브라우저는 모두 crypto.getRandomValues를 지원합니다.

자주 묻는 질문

자주 묻는 질문

랜덤 돌림판은 정말 랜덤인가요?

이 사이트의 돌림판은 그렇습니다. 매번 브라우저의 암호학적으로 안전한 난수 crypto.getRandomValues로 애니메이션 전에 멈출 위치를 정합니다. 각 칸의 당첨 확률은 면적에 비례하고, 같은 크기의 칸은 확률도 같습니다.

사이트 운영자나 돌리는 사람이 조작할 수 있나요?

추첨이 브라우저에서 이루어지기 때문에 저희 서버가 결과를 정하거나 바꾸지 않습니다. 돌리는 사람도 조작할 수 없습니다. 누르는 타이밍이나 길게 누르기는 무작위 멈춤 각도에 영향을 주지 않습니다. 코드는 브라우저 개발자 도구로 언제든 확인할 수 있습니다.

같은 이름이 두 번 연속 나온 이유는 뭔가요?

난수에는 기억이 없기 때문입니다. 항목이 8개면 같은 항목이 두 번 연속 나올 확률은 12.5%이고, 20번 돌리면 연속 당첨이 한 번 이상 나올 확률이 약 92%입니다. 중복을 원하지 않으면 뽑힌 항목을 매번 제외하세요.

오래 돌리거나 빨리 돌리면 확률이 달라지나요?

아니요. 멈춤 각도는 돌리기를 누르는 순간 정해집니다. 시간, 속도, 감속 방식은 움직임의 모양만 바꿀 뿐 멈추는 곳은 바꾸지 않습니다.

돌림판의 확률 설정은 어떻게 동작하나요?

항목에 퍼센트를 넣으면 그 비율만큼 원을 차지하도록 칸이 그려지며, 합계는 100이어야 합니다. 30%로 설정한 항목은 장기적으로 10번 중 3번쯤 당첨되지만 짧게 보면 그보다 많거나 적을 수 있습니다.

시뮬레이터의 카이제곱 값과 판정은 무슨 뜻인가요?

칸마다 실제 횟수와 기대 횟수의 차이를 제곱해 기대 횟수로 나눈 뒤 모두 더한 값이 카이제곱입니다. 해당 칸 수의 5% 기준값보다 낮으면 공정한 난수와 어긋나지 않는 결과입니다. 공정해도 약 20번에 1번은 통과하지 못하니 여러 번 돌려 보고 판단하세요.

당첨자를 Math.random으로 뽑나요?

아니요. Math.random은 주사위 기울기나 구슬 흔들림처럼 결과가 나온 뒤의 시각 효과에만 씁니다. 결과는 crypto.getRandomValues에서 나오며, Web Crypto를 지원하지 않는 아주 오래된 브라우저에서만 Math.random으로 대체됩니다.