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

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 을 사용하라.