Java Language
난수 생성
수색…
비고
아무것도 실제로 무작위가 아니기 때문에 javadoc은 그 숫자를 의사 난수라고 부릅니다. 이러한 숫자는 의사 난수 생성기를 사용하여 만듭니다.
가짜 난수
Java는 utils
패키지의 일부로 Random
이라는 이름의 기본 의사 난수 생성기를 제공합니다. 이 객체는 임의의 내장 숫자 데이터 유형 ( int
, float
등)으로 의사 임의 값을 생성하는 데 사용할 수 있습니다. 또한이를 사용하여 임의의 부울 값 또는 임의의 바이트 배열을 생성 할 수 있습니다. 사용 예는 다음과 같습니다.
import java.util.Random;
...
Random random = new Random();
int randInt = random.nextInt();
long randLong = random.nextLong();
double randDouble = random.nextDouble(); //This returns a value between 0.0 and 1.0
float randFloat = random.nextFloat(); //Same as nextDouble
byte[] randBytes = new byte[16];
random.nextBytes(randBytes); //nextBytes takes a user-supplied byte array, and fills it with random bytes. It returns nothing.
참고 :이 클래스는 상당히 낮은 품질의 의사 난수를 생성하기 때문에 암호 연산이나 높은 품질의 임의성이 중요한 기타 상황에서 임의의 숫자를 생성하는 데 사용해서는 안됩니다 ( SecureRandom
클래스를 사용하려는 경우, 아래에서 언급했듯이). "안전"과 "안전하지 않은"임의성의 구분에 대한 설명은이 예제의 범위를 벗어납니다.
특정 범위의 의사 난수
Random
의 메소드 nextInt(int bound)
는 상위 배타적 경계, 즉 반환 된 임의 값이보다 작은 숫자 여야합니다. 다만, nextInt
메소드 만이 바인딩을 받아들입니다. nextLong
, nextDouble
등은하지 않습니다.
Random random = new Random();
random.nextInt(1000); // 0 - 999
int number = 10 + random.nextInt(100); // number is in the range of 10 to 109
Java 1.7부터는 ThreadLocalRandom
( source )을 사용할 수도 있습니다. 이 클래스는 thread 세이프 인 PRNG (의사 난수 생성 프로그램)를 제공합니다. 이 클래스의 nextInt
메소드는 상한과 하한을 모두 받아들입니다.
import java.util.concurrent.ThreadLocalRandom;
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
ThreadLocalRandom.current().nextInt(min, max + 1);
공식 문서 에서는 bound
가 2 30 +1 (강조가 추가됨) 근처에있을 때 nextInt(int bound)
가 이상한 것을 할 수 있다고 명시합니다.
알고리즘은 약간 까다 롭습니다. (2 ^ 31이 n으로 나눌 수 없다는 사실 때문에) 불균등 한 분포를 초래할 수있는 값을 거부합니다 . 값이 거부되는 확률은 n에 달려 있습니다. 최악의 경우는 n = 2 ^ 30 + 1이며,이 경우 거부의 확률은 1/2이고 루프가 끝나기 전에 예상되는 반복 횟수는 2입니다.
즉, 경계를 지정하면 nextInt
메소드의 성능이 약간 nextInt
,이 성능 저하는 bound
가 최대 int 값의 절반 bound
가까워짐에 따라 더 두드러지게됩니다.
암호로 안전한 의사 난수 생성
Random
과 ThreadLocalRandom
은 일상적으로 사용하기에 충분하지만 큰 문제가 있습니다. 선형 합동 생성기를 기반으로합니다. 알고리즘은 출력이 다소 쉽게 예측 될 수 있습니다. 따라서이 두 클래스는 키 생성과 같은 암호화 용도에 적합 하지 않습니다 .
예측하기가 매우 어려운 PRNG가 필요한 상황에서 java.security.SecureRandom
을 사용할 수 있습니다. 이 클래스의 인스턴스에 의해 생성 된 난수를 예측하는 것은 클래스를 암호 학적으로 안전한 것으로 분류하기에 충분합니다.
import java.security.SecureRandom;
import java.util.Arrays;
public class Foo {
public static void main(String[] args) {
SecureRandom rng = new SecureRandom();
byte[] randomBytes = new byte[64];
rng.nextBytes(randomBytes); // Fills randomBytes with random bytes (duh)
System.out.println(Arrays.toString(randomBytes));
}
}
암호 해독 외에도 SecureRandom
은 2 48 의 Random
주기에 비해 2 160 의 거대한 기간을 갖습니다. 그러나 Mersenne Twister 와 Xorshift 와 같은 Random
과 다른 선형 PRNG보다 상당히 느린 단점이 있습니다.
SecureRandom의 구현은 플랫폼과 프로 바이더에 의존하고 있습니다. 디폴트의 SecureRandom
( sun.security.provider.SecureRandom
내의 SUN
프로 바이더에 의해 지정된다).
- 유닉스 계열 시스템에서
/dev/random
및 / 또는/dev/urandom
됩니다. - Windows에서 CryptoAPI의
CryptGenRandom()
호출을 시드 합니다 .
중복되지 않은 난수 선택
/**
* returns a array of random numbers with no duplicates
* @param range the range of possible numbers for ex. if 100 then it can be anywhere from 1-100
* @param length the length of the array of random numbers
* @return array of random numbers with no duplicates.
*/
public static int[] getRandomNumbersWithNoDuplicates(int range, int length){
if (length<range){
// this is where all the random numbers
int[] randomNumbers = new int[length];
// loop through all the random numbers to set them
for (int q = 0; q < randomNumbers.length; q++){
// get the remaining possible numbers
int remainingNumbers = range-q;
// get a new random number from the remainingNumbers
int newRandSpot = (int) (Math.random()*remainingNumbers);
newRandSpot++;
// loop through all the possible numbers
for (int t = 1; t < range+1; t++){
// check to see if this number has already been taken
boolean taken = false;
for (int number : randomNumbers){
if (t==number){
taken = true;
break;
}
}
// if it hasnt been taken then remove one from the spots
if (!taken){
newRandSpot--;
// if we have gone though all the spots then set the value
if (newRandSpot==0){
randomNumbers[q] = t;
}
}
}
}
return randomNumbers;
} else {
// invalid can't have a length larger then the range of possible numbers
}
return null;
}
이 메서드는 요청한 길이의 크기를 가진 배열을 반복하여 작동하고 가능한 숫자의 나머지 길이를 찾습니다. 가능한 번호 newRandSpot
의 임의의 수를 설정하고 그 번호를 촬영되지 않은 숫자 왼쪽에서 찾습니다. 범위를 반복하고 해당 숫자가 이미 사용되었는지 확인하기 위해 점검합니다.
예를 들어 범위가 5이고 길이가 3이고 이미 숫자 2를 선택한 경우 남은 숫자가 4 개이므로 1에서 4 사이의 임의의 숫자를 얻고 숫자를 건너 뛰는 범위 (5)를 반복합니다. 우리는 이미 사용했습니다 (2).
이제 1과 4 사이에서 선택되는 다음 숫자가 3이라고 가정 해 봅시다. 첫 번째 루프에서 아직 얻지 못한 1을 얻습니다. 3에서 1을 제거 할 수 있습니다 2. 이제 두 번째 루프에서 2를 얻습니다 그래서 우리는 아무것도하지 않습니다. 우리는 4가 될 때까지이 패턴을 따른다. 1을 제거하면 0이되어 새로운 randomNumber를 4로 설정한다.
지정된 시드로 난수 생성하기
//Creates a Random instance with a seed of 12345.
Random random = new Random(12345L);
//Gets a ThreadLocalRandom instance
ThreadLocalRandom tlr = ThreadLocalRandom.current();
//Set the instance's seed.
tlr.setSeed(12345L);
동일한 시드를 사용하여 임의의 숫자를 생성 할 때마다 동일한 숫자가 반환되므로 모든 Random
인스턴스에 대해 서로 다른 시드를 설정하면 중복되는 숫자로 끝내지 않으려면 좋은 생각입니다.
모든 호출에 대해 다른 Long
을 얻는 좋은 방법은 System.currentTimeMillis()
.
Random random = new Random(System.currentTimeMillis());
ThreadLocalRandom.current().setSeed(System.currentTimeMillis());
apache-common lang3을 사용하여 난수 생성하기
우리는 org.apache.commons.lang3.RandomUtils
를 사용하여 한 줄로 난수를 생성 할 수 있습니다.
int x = RandomUtils.nextInt(1, 1000);
nextInt(int startInclusive, int endExclusive)
메소드는 범위를 취합니다.
int와는 별도로이 클래스를 사용하여 long
, double
, float
및 bytes
를 임의로 생성 할 수 있습니다.
RandomUtils
클래스에는 다음 메소드가 포함되어 있습니다.
static byte[] nextBytes(int count) //Creates an array of random bytes.
static double nextDouble() //Returns a random double within 0 - Double.MAX_VALUE
static double nextDouble(double startInclusive, double endInclusive) //Returns a random double within the specified range.
static float nextFloat() //Returns a random float within 0 - Float.MAX_VALUE
static float nextFloat(float startInclusive, float endInclusive) //Returns a random float within the specified range.
static int nextInt() //Returns a random int within 0 - Integer.MAX_VALUE
static int nextInt(int startInclusive, int endExclusive) //Returns a random integer within the specified range.
static long nextLong() //Returns a random long within 0 - Long.MAX_VALUE
static long nextLong(long startInclusive, long endExclusive) //Returns a random long within the specified range.