VBA로 추첨 프로그램이나 무작위 배정 매크로를 만들어 놓고 몇 번 돌려 보면 이상한 걸 발견하게 됩니다. 파일을 닫았다가 다시 열고 실행하면 처음 나오는 숫자가 지난번이랑 똑같아요. 두 번째, 세 번째 숫자도 순서까지 그대로입니다. 무작위라더니 매번 같은 결과가 나오는 셈이죠.
Rnd 함수가 고장 난 게 아니라 원래 그렇게 동작합니다. 컴퓨터가 만드는 난수는 진짜 우연이 아니라, 시작값(시드) 하나를 정해 놓고 정해진 계산식으로 다음 숫자를 계속 만들어 내는 방식입니다. 시작값이 같으면 나오는 숫자 줄도 처음부터 끝까지 같습니다. VBA는 시드를 따로 지정하지 않으면 매번 같은 초기값에서 출발하기 때문에, 프로젝트가 새로 시작될 때마다 똑같은 숫자 줄이 반복됩니다.
Randomize 를 빼고 돌려 보면
예를 들어 1~10 사이 숫자 다섯 개를 뽑는 코드를 이렇게 짰다고 해 보겠습니다. Randomize 줄은 일부러 주석 처리했습니다.
Sub RandomTest()
Dim i As Integer
'Randomize
For i = 1 To 5
Debug.Print Int(Rnd * 10) + 1
Next i
End Sub
이 상태로 파일을 열고 실행하고, 닫았다가 다시 열어 실행하기를 반복하면 첫 숫자가 항상 같게 나옵니다. 아래 영상에서는 매번 첫 값이 8로 고정되는 모습입니다.
실행 영상 1: Randomize 를 주석 처리한 상태에서 첫 값이 매번 8로 나오는 모습
한 번 실행 안에서는 숫자가 계속 바뀌니까 테스트할 때는 잘 모릅니다. 문제는 사용자가 파일을 열자마자 매크로를 한 번 돌리는 경우예요. 추첨 결과가 늘 같은 사람에게 몰리는 식으로 티가 납니다.
Randomize 한 줄이 하는 일
주석을 풀어서 Randomize 를 실행하게 하면 결과가 달라집니다. 인수 없이 Randomize 를 부르면 시스템 타이머 값을 새 시드로 씁니다. 실행할 때마다 시각이 다르니 시드도 달라지고, 첫 숫자부터 매번 다른 값이 나옵니다. 영상에서는 이번엔 8이 아니라 6이 먼저 나왔습니다.
실행 영상 2: Randomize 주석을 해제하자 첫 값이 6으로 바뀐 모습
Randomize 는 매크로 시작 부분에서 한 번만 부르면 충분합니다. 반복문 안에 넣어서 숫자를 뽑을 때마다 부르는 코드를 가끔 보는데, 타이머 값이 거의 같은 짧은 간격으로 시드를 계속 다시 잡으면 오히려 비슷한 값이 연달아 나올 수 있습니다.
일부러 같은 순서를 다시 만들고 싶을 때
반대로 테스트할 때는 매번 같은 난수 순서가 나와야 디버깅이 편합니다. 이럴 때는 Randomize 에 숫자를 주기 직전에 Rnd 를 음수 인수로 한 번 호출합니다. 이렇게 하면 같은 숫자를 넣었을 때 같은 순서가 다시 나옵니다.
Sub RepeatableRandom()
Dim i As Integer
Rnd -1
Randomize 42
For i = 1 To 5
Debug.Print Int(Rnd * 10) + 1
Next i
End Sub
Randomize 42 만 쓰고 Rnd -1 을 빼면 같은 순서가 보장되지 않는다는 점이 헷갈리기 쉬운 부분입니다.
한 가지 더 기억할 점은 Rnd 로 만든 숫자는 계산식과 시드를 알면 예측이 가능하다는 겁니다. 경품 추첨이나 화면용 무작위 정렬 정도에는 충분하지만, 임시 비밀번호나 인증 토큰처럼 보안이 걸린 값을 만드는 데는 쓰면 안 됩니다. .NET 쪽이라면 그런 용도에는 RandomNumberGenerator 같은 암호화용 난수 클래스를 씁니다.
참고로 워크시트 함수 RAND(), RANDBETWEEN() 은 VBA의 Rnd 와는 별개라서 Randomize 의 영향을 받지 않습니다. 셀 수식으로 난수를 쓰고 있다면 이 문제와는 상관이 없어요.