주인장의 프로그래밍 개발 공부방

[자바 프로그래밍 기초] 7. 난수를 뽑아주는 Random 클래스 본문

Java/자바 프로그래밍 기초

[자바 프로그래밍 기초] 7. 난수를 뽑아주는 Random 클래스

거신 2023. 1. 31. 02:44

지난번에 자바에서 난수를 뽑아내는 방법으로 Math.random()이라는 메소드에 대해서 알아봤었다.

그리고 이번에는 자바에서 난수를 뽑아내는 다른 방법인

Random 클래스를 이용한 방법에 대해서 알아보도록 하자.

 

1. Random 클래스

 

그렇다면 일단 Random 클래스에 대해서 먼저 알아보자.

Random 클래스는 클래스의 이름 그대로 난수와 관련된 메소드들로 구성되어 있는 클래스이다.

(자바 레퍼런스 문서나 JDK 내에서 확인해보면 알겠지만

Random 클래스에는 필드가 없고 생성자와 메소드만 있다.)

 

Math.random()은 단순히 double 타입의 난수를 뽑아주는 것만 가능하지만

Random 클래스는 난수와 관련된 메소드들이 포함되어 있기 때문에

다양한 조건의 난수를 뽑아내는 것이 가능하다.

그리고 Math.random()을 뜯어보면 Random 클래스를 이용해 만들어진 녀석이다.

(오??)

 

거두절미하고 일단 앞서 Math.random()을 이용해서 난수를 뽑아낸 것과 동일하게

Random 클래스를 이용해 난수를 뽑아보자.

 

예시 1)

double randNum = Math.random(); // 0.xxxxx.....

int num_1 = (int) (randNum * 5);     // 0 ~ 4 사이의 숫자를 뽑아냄
int num_2 = (int) (randNum * 5) + 1; // 1 ~ 5 사이의 숫자를 뽑아냄

// 여기까지가 Math.random()을 이용해 난수를 뽑아내는 방법

// 여기부터가 Random 클래스를 이용해 난수를 뽑아내는 방법
Random random = new Random();

random.nextDouble();		// Math.random()과 동일한 결과 형태 출력

int num_1 = random.nextInt(5);			// (int) (randNum * 5) 와 동일한 결과 형태 출력
int num_2 = random.nextInt(5) + 1;		// (int) (randNum * 5) + 1 와 동일한 결과 형태 출력

 

비교를 위해서 앞의 내용이었던 Math.random()의 내용과 함께 작성해 보았다.

주석의 내용에서 알 수 있듯이 Random 클래스를 이용해 동일한 결과 형태를 출력하는

소스로 작성해 보았다.

 

우선 Random 클래스를 사용하기 위해선

Random 클래스인스턴스를 먼저 생성해줘야 한다.

Math.random()과는 다르게 각각의 메소드들이 static이 아니기 때문에

예시 1)과 같이 인스턴스로 생성해서 사용해야 한다는 것을 먼저 잊지 말자.

(심심풀이로 Random 클래스 내부를 살펴보면 도움이 될 것이다.

귀찮으면 어쩔 수 없고)

그리고 Random 클래스Math.random()과 같이 그냥 사용할 수는 없고

역시 소스파일 내에서 java.util.Random을 import 해야 한다는 것도 함께 알고 있자.

 

예시 1)을 이용해 하나씩 살펴보면

Math.random()과 동일한 형태의 결과를 출력해 주는 random.nextDouble()

Math.random()과 똑같이 double형의 임의의 숫자를 리턴하고,

리턴하는 숫자의 범위 또한 0.0 이상 1.0 미만이 되겠다.

실제로 Java docs 등의 문서에서 Math.random()을 찾아보면

참고 항목으로 Random.nextDouble() (<- 사실 이 표현 잘못된 표현이다.)

을 표시하고 있고 반대로 Random 클래스에서 nextDouble()을 찾아보면

참고 항목으로 Math.random()을 표시하고 있다.

 

그리고 잠깐 지나가는 내용이었지만 좀 자세히 봤던 사람들이라면

이 부분이 기억이 날 것이다.

 

예시 2)

public static double random() {
     return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}

예시 2)Math.random()의 메소드 구현부인데 호출한 메소드가 nextDouble()이다.

여기에서의 nextDouble()예시 1)에서 봤던 random.nextDouble()과 동일한 메소드이다.

(JDK 안에서 찾아가 보면 똑같은 곳에 당도할 수 있을 것이다.)

 

그리고 등장한 메소드가 바로 random.nextInt()이다.

 

2. Random 클래스의 메소드

 

우리가 Math.random()을 이용했을 때 뽑아낸 난수를 정수형태로 변환하기 위해서

일련의 복잡한 계산식을 이용하고 강제 캐스팅까지 해야 했던 것을

한 번에 해주는 메소드가 바로 random.nextInt()이다.

random.nextDouble()과 같이 random.nextInt()는 메소드명 그대로 int형의 임의의 숫자를 뽑아낸다.

 

그런데 여기서 주의할 점은

random.nextInt()가 리턴하는 숫자의 범위이다.

random.nextInt()는 다른 메소드와는 다르게 int 타입의 범위 전체를 대상으로

임의의 정수를 리턴한다는 점이다.

다시 말해, random.nextInt()의 숫자의 범위는

–2,147,483,648 ~ 2,147,483,647

인 것이다.

 

정말 어마어마한 범위 내에서 임의의 숫자를 뽑아주는데

현재의 우리에겐 이 정도의 숫자는 필요하지 않는다는 걸 다들 알고 있을 것이다.

그래서 Random 클래스에서는 이렇게 무지막지한 숫자가 아닌

도가 지나치지 않는 정도의 숫자를 뽑아낼 수 있도록 다른 메소드를 제공하고 있다.

 

random.nextInt()에서 매개변수를 추가하여 오버로딩된 메소드인

random.nextInt(int bound)을 제공하고 있다.

여기에서 매개변수 bound난수의 범위를 지정하는 변수가 되겠다.

즉, 0에서 bound - 1까지의 범위를 지정할 수 있는 것이다.

Math.random()에서 우리가 뽑아낸 난수에 *(곱하기)를 하는 것과 동일한 기능이다.

생성한 임의의 수에 곱하는 수는 뽑아낼 숫자의 개수라고 했는데

여기에서는 bound가 이 역할을 하고 있는 것이다.

 

뒤에 더하기는 Math.random()에서와 동일하게

뽑아낼 숫자의 범위에서 시작하는 숫자를 말하는 것이다.

 

이렇듯 Random 클래스에서는 생각보다 다양하게 난수를 뽑아내는

메소드들을 제공하고 있다.

물론 위의 예시들이 Math.random()같이

Random 클래스의 메소드들을 활용해 동일한 기능처럼 작성한 것이기 때문에

그게 그거 같아 보일 수도 있겠지만

분명한 것은 Math.random()보다 Random 클래스가 활용도가 더 높다는 점이다.

아직은 크게 잘 느껴지지 않을 수도 있는데

일단 더 심오하게 알아보기 전에 활용도가 조금은 느껴지도록

Random 클래스의 주요 메소드들을 살펴보도록 하자.

 

표 1)

리턴타입 메소드 설명 리턴 숫자의 범위
int nextInt() int 타입 난수 리턴 –2,147,483,648 ~ 2,147,483,647
int nextInt(int bound) 0 ~ bound - 1 사이의 난수 리턴 0 ~ (bound - 1)
long nextLong() long 타입 난수 리턴 -9223372036854775808
~ 9223372036854775807
float nextFloat() float 타입 난수 리턴 0.0F ~ 0.999.....F
double nextDouble() double 타입 난수 리턴 0.0D ~ 0.999......D
double nextGaussian() 표준정규분포형으로 난수 리턴 표준정규분포
(평균이 0이고, 표준편차가 1인 범위)
boolean nextBoolean() true 또는 false를 랜덤으로 리턴 true / false
void nextBytes(byte[] bytes) byte 배열에 난수의 byte로 채움  
void setSeed() 시드 지정  

 

표 1)에서도 확인할 수 있듯이

다양한 타입의 난수를 리턴하는 메소드들을 제공하고 있다.

그렇기 때문에 따로 계산을 한다든가, 캐스팅 등의 변환을 하지 않고

Random 클래스인스턴스를 이용해

다양한 타입의 난수를 만들어 낼 수 있다.

거기에 true 또는 false까지 랜덤으로 뽑아낼 수 있도록

boolean 타입의 메소드까지 제공하고 있으니 이 얼마나 활용도가 높지 않을 수 있겠는가!

심지어 표준정규분포형의 난수까지 제공해주고 있다.

(사실 저게 얼마나, 어디에 활용될지는 모르겠지만....)

 

아무튼 위와 같이 다양한 메소드들을 제공하고 있고

우리들은 필요에 맞게 메소드들을 활용만 하면 되는 것이다.

물론 이렇게 다양한 메소드들을 제공한다고 해서

무조건 Random 클래스가 좋다는 의미는 아니다.

필요에 따라선 간단하게 난수를 뽑아낼 때는 인스턴스 생성 없이

바로 Math.random()을 호출해서 난수를 뽑아내는 것도 좋은 방법이다.

 

그런데 이렇게만 보면 Random 클래스Math.random()이든 별 차이가 없어 보이지만

위의 표에 얼굴을 잠깐 비춘 녀석 때문에

Random 클래스Math.random()의 차이가 나타나고

Random 클래스의 활용도를 나타낼 수 있다고 볼 수 있겠다.

그것이 바로

setSeed()

라는 메소드이다.

Comments