추첨 작동 원리
'무작위니 믿으세요'라는 말 대신, 실제 구현 방식을 명확히 공개합니다.
당첨자는 돌림판이 움직이기 전에 결정됩니다
모든 회전은 당첨자를 먼저 뽑은 후 돌림판 애니메이션의 최종 멈춤 위치를 그곳으로 맞춥니다. 이 순서 덕분에 돌림판을 얼마나 세게 돌리든 결과에 영향을 주지 않으며, 애니메이션 줄이기 설정을 켜도 별도의 코드 경로 없이 동일한 추첨 결과만 바로 확인할 수 있습니다. 화면에 보이는 회전 연출은 이미 결정된 결과를 재현하는 과정이며, 그렇기 때문에 위에서 약속한 공정성을 완벽하게 지킬 수 있습니다.
무작위성의 출처
난수 생성에는 Math.random 대신 브라우저의 암호화 난수 생성기인 crypto.getRandomValues를 사용합니다. Math.random은 예측 가능성이 허용되며 일부 자바스크립트 엔진에서는 실제로 예측할 수 있습니다. 공정한 추첨에서는 이것이 매우 중요합니다.
일부 난수를 버리는 이유
나머지 연산(modulo)을 통해 32비트 난수를 '0부터 11까지의 숫자'로 변환하는 방식에는 미세한 불공정성이 존재합니다. 2³²은 12로 나누어떨어지지 않기 때문에 앞쪽 몇 개 항목이 1번의 기회를 더 얻게 됩니다. 12칸 돌림판에서 이 편향은 매우 작지만 실제로 존재하며, 항상 특정 칸에만 유리하게 작용하고 아무리 많이 돌려도 상쇄되지 않습니다. 따라서 TossWheel은 단순히 나머지를 취하는 대신, 고르지 못한 꼬리 영역에 떨어지는 난수를 버리고 다시 추출합니다. 간혹 추가 추첨이 발생하지만, 완벽하게 균등한 확률을 보장합니다.
const limit = Math.floor(2 ** 32 / max) * max;
let draw = crypto.getRandomValues(new Uint32Array(1))[0];
while (draw >= limit) {
draw = crypto.getRandomValues(new Uint32Array(1))[0];
}
return draw % max;가중치와 구역 크기가 정확히 일치하는 이유
가중치 추첨은 각 가중치의 총합을 구한 뒤, 동일한 거부 샘플링 방식을 통해 해당 합계 미만의 균등 난수를 뽑아 어느 항목의 구간에 속하는지 판정합니다. 돌림판은 각 항목의 비율을 원의 면적에 정확히 일치하도록 그립니다. 확률은 다른데 겉보기에만 똑같아 보이는 돌림판은 기만적인 방식입니다. 따라서 구역의 면적과 당첨 확률은 정책적인 눈속임이 아닌 구조적으로 완전히 일치합니다.
목록 데이터는 브라우저 밖으로 전송되지 않습니다
회원가입이 필요 없으며 서버에 어떠한 데이터도 저장하지 않습니다. 목록은 사용 중인 브라우저에만 보관되고, 공유 링크는 돌림판 정보 자체를 URL 안에 담아 전달합니다. 추첨에 관한 어떤 기록도 서버에서 확인할 수 없습니다.
직접 확인해 보세요
추첨 로직은 수십 줄의 코드로 작성되어 있으며, 위의 모든 원칙은 검증 테스트를 거칩니다. 만약 편향이 발생하면 테스트는 즉시 실패합니다. 모듈로 편향(modulo bias)을 유발하도록 설계된 난수를 주입하고도 결과가 균등하게 유지되는지 확인하는 테스트도 포함되어 있습니다.
돌림판은 최대 1000개 항목을 담을 수 있으며, 항목이 1개이든 1000개이든 동일한 추첨 방식이 적용됩니다.