무엇을 뽑느냐보다 어떻게 뽑느냐
추첨이나 배정에 쓸 숫자를 고를 때 대부분의 온라인 도구는 프로그래밍 언어에 기본으로 들어 있는 난수 함수를 씁니다. 자바스크립트의 Math.random()이 대표적입니다. 문제는 이것이 진짜 무작위가 아니라 정해진 공식으로 만들어 낸 수열이라는 점입니다. 시작값이 같으면 같은 수열이 그대로 다시 나옵니다.
혼자 주사위를 굴리는 용도라면 상관없습니다. 그러나 경품이 걸려 있거나, 순번이 이해관계와 연결되거나, 나중에 결과에 이의가 제기될 수 있는 자리라면 이야기가 다릅니다. 이 도구는 그런 자리를 염두에 두고 브라우저의 Web Crypto API(crypto.getRandomValues)를 사용합니다. 운영체제가 하드웨어 잡음에서 모은 엔트로피를 받아 쓰기 때문에 다음 값을 예측할 수 없습니다.
범위를 자를 때 생기는 치우침
안전한 난수를 받아 왔다고 끝이 아닙니다. 1부터 100까지가 필요할 때 흔히 나머지 연산으로 범위를 줄이는데, 여기서 조용히 편향이 생깁니다. 0에서 255까지 나오는 한 바이트를 100으로 나눈 나머지를 쓰면, 0에서 55까지의 값은 세 번씩(0, 100, 200 등) 대응되고 56에서 99까지는 두 번씩만 대응됩니다.
결과적으로 작은 숫자가 큰 숫자보다 50% 더 자주 나옵니다. 눈으로는 알아채기 어렵지만 측정하면 분명히 드러나는 차이이고, 참가자가 많은 추첨에서는 명단의 특정 위치가 유리해진다는 뜻이 됩니다.
이 도구는 거부 표집(rejection sampling)으로 이를 없앱니다. 치우침이 생기는 구간의 값이 나오면 버리고 다시 뽑는 방식입니다. 위 예에서는 256번 중 56번 정도를 버리게 되지만, 난수 한 바이트를 더 만드는 비용은 나노초 단위여서 체감되지 않습니다. 공정함을 그 정도 값에 살 수 있다면 사는 편이 맞습니다.
어디에 쓰나
- 경품과 추첨. 참가자에게 번호를 매기고 그 범위에서 뽑습니다. 중복 없이 여러 명을 뽑아야 한다면 중복 제거를 켜 두세요.
- 조 편성과 순서 정하기. 발표 순서나 청소 당번처럼 누구도 손대지 않았다는 점이 중요한 배정에 적합합니다.
- 표본 추출. 전수 검사가 어려울 때 검사할 항목을 고르는 데 씁니다. 사람이 임의로 고르면 무의식적인 편향이 들어갑니다.
- 테스트 데이터. 다만 재현이 필요한 자동화 시험에서는 오히려 시드를 고정한 의사난수가 맞습니다. 매번 달라지면 실패를 재현할 수 없기 때문입니다.
공개 추첨에서 지켜야 할 것
도구가 공정해도 절차가 공정하지 않으면 소용이 없습니다. 뽑기 전에 범위와 뽑을 개수를 먼저 공지하고, 결과가 마음에 들지 않는다고 다시 돌리지 않는 것이 핵심입니다. 여러 번 돌린 뒤 마음에 드는 것을 고르면 그 순간 무작위가 아니게 됩니다.
기록을 남기려면 화면을 녹화하거나 참관자를 두는 편이 좋습니다. 이 도구는 결과를 서버에 저장하지 않으므로, 나중에 증빙이 필요하다면 그 자리에서 남겨 두어야 합니다.