만약, 무작위 정수 하나를 생성하고자 할 때 흔히 다음과 같이 작성할 것이다.

static Random rnd = new Random();

static int random(int n) {
	return Math.abs(rnd.nextInt()) % n;
}

많은 프로그래머가 위와 같이 작성하지만, 해당 코드는 문제를 세 가지나 내포하고 있다.

  1. n이 그리 크지 않은 2의 제곱수라면, 얼마 지나지 않아 같은 수열이 반복된다.

    2의 제곱수인 n에 대한 nextInt() 함수의 내부 동작 방식 때문에 발생한다.

    nextInt()는 32비트 정수를 반환하는데, 이 정수의 비트 수는 2의 제곱수이다. 따라서 n이 2의 제곱수이고 그 크기가 작다면, 생성된 무작위 수열은 실제로 그리 길지 않을 수 있다. 즉, n이 2의 제곱수인 경우 nextInt() 함수가 반환하는 범위 내에서 n으로 나누었을 때 가능한 나머지 값들의 수가 제한적이므로, 동일한 패턴의 수열이 빠르게 반복될 수 있다.

  2. n이 2의 제곱수가 아니라면 몇몇 숫자가 평균적으로 더 자주 반환된다.

    ‘ 모듈로 연산의 편향 ‘ 때문에 발생한다.

  3. 지정한 범위 밖의 수가 나올 수 있다.

    rnd.nextInt()는 음수를 포함한 모든 정수를 반환할 수 있다. 따라서, Math.abs() 함수를 사용해 음수를 양수로 변환하면, 이 변환 과정에서 반환된 값의 범위가 0에서 n - 1까지가 아닌 0에서 Integer.MAX_VALUE까지로 확대되는 문제가 발생한다. 이는 rnd.nextInt()가 음수를 반환할 때 발생하는데, 이 때 Math.abs()함수를 통해 음수가 양수로 변환되면서 원래 범위를 벗어난 값이 생성될 수 있다.

[참고: https://joyerim.tistory.com/m/104]

위의 문제를 해결하기 위해서는 의사난수 생성기, 정수론, 2의 보수 계산 등에 조예가 깊어야 한다.

하지만, 직접 해결하지 않고 Random.nextInt(int) 를 사용하면 해결할 수 있다!

Untitled

이 메서드의 자세한 동작 방식을 몰라도 사용하는 데 문제가 없다.

자바 7부터는 Random 을 더 이상 사용하지 않는 것이 좋다. 대신, ThreadLocalRandom 을 사용하라.