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
, как указано ниже). Объяснение различия между «безопасным» и «неуверенным» случайным образом выходит за рамки этого примера.
Псевдо случайные числа в определенном диапазоне
Метод nextInt(int bound)
Random
принимает верхнюю эксклюзивную границу, то есть число, которое возвращаемое случайное значение должно быть меньше. Однако только метод nextInt
принимает 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
( источник ). Этот класс обеспечивает поточно-безопасный 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);
Обратите внимание, что в официальной документации указано, что nextInt(int bound)
может делать странные вещи, когда bound
около 2 30 +1 (выделено курсивом):
Алгоритм немного сложный. Он отклоняет значения, которые приведут к неравномерному распределению (из-за того, что 2 ^ 31 не делится на n). Вероятность отклонения значения зависит от n. Наихудший случай равен n = 2 ^ 30 + 1, для которого вероятность отклонения равна 1/2, а ожидаемое число итераций до окончания цикла равно 2.
Другими словами, указание границы (немного) уменьшит производительность метода nextInt
, и это снижение производительности станет более выраженным, так как bound
приближается к половине максимального значения int.
Создание криптографически безопасных псевдослучайных чисел
Random
и ThreadLocalRandom
достаточно хороши для повседневного использования, но у них есть большая проблема: они основаны на линейном конгруэнтном генераторе , алгоритме, выход которого можно предсказать довольно легко. Таким образом, эти два класса не подходят для криптографических целей (таких как генерация ключей).
Можно использовать java.security.SecureRandom
в ситуациях, когда требуется 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 160 , по сравнению с периодом Random
s 2 48 . У этого есть один недостаток быть значительно медленнее, чем Random
и другие линейные PRNG, такие как Mersenne Twister и Xorshift .
Обратите внимание, что реализация SecureRandom зависит от платформы и поставщика. По умолчанию SecureRandom
(данный поставщиком SUN
в sun.security.provider.SecureRandom
):
- на Unix-подобных системах, засеянных данными из
/dev/random
и / или/dev/urandom
. - в Windows, засеянных вызовами
CryptGenRandom()
в CryptoAPI .
Выбор случайных чисел без дубликатов
/**
* 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, который еще не был взят, поэтому мы можем удалить 1 из 3, сделав его 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.