تعارف
دنیا میں بہت سے سائنس ایسے ہیں جو نظریہ امکان کا مطالعہ کرتے ہیں۔ اور علوم مختلف حصوں پر مشتمل ہیں۔ مثال کے طور پر، ریاضی میں ایک الگ سیکشن ہے جو بے ترتیب واقعات، مقدار وغیرہ کے مطالعہ کے لیے مختص ہے۔ لیکن سائنس کو ہلکے سے نہیں لیا جاتا۔ اس معاملے میں، امکان کا نظریہ اس وقت شکل اختیار کرنا شروع ہوا جب لوگوں نے یہ سمجھنے کی کوشش کی کہ موقع کا کھیل کھیلتے ہوئے ڈائس پھینکنے میں کیا نمونے ہیں۔ اگر آپ قریب سے دیکھیں تو ہمارے اردگرد بہت سی بظاہر بے ترتیب چیزیں نظر آتی ہیں۔ لیکن بے ترتیب ہر چیز مکمل طور پر بے ترتیب نہیں ہے۔ لیکن بعد میں اس پر مزید۔ جاوا پروگرامنگ لینگویج میں JDK کے پہلے ورژن سے شروع ہونے والے بے ترتیب نمبروں کے لیے بھی حمایت حاصل ہے۔ جاوا میں بے ترتیب نمبروں کو
java.util.Random کلاس کا استعمال کرتے ہوئے استعمال کیا جا سکتا ہے ۔ جانچ کے لیے، ہم
tutorialspoint java online compiler استعمال کریں گے ۔ یہاں روسی میں "نرد" یا کیوبز پھینکنے کی تقلید کے لیے
رینڈم کا استعمال کرنے کی ایک قدیم مثال ہے :
import java.util.Random;
public class HelloWorld{
public static void main(String []args){
Random rnd = new Random();
int number = rnd.nextInt(6) + 1;
System.out.println("Random number: " + number);
}
}
ایسا لگتا ہے کہ یہ Random کی تفصیل کا اختتام ہوسکتا ہے ، لیکن یہ اتنا آسان نہیں ہے۔ آئیے جاوا API میں
java.util.Random کلاس کی تفصیل کھولتے ہیں۔ اور یہاں ہم دلچسپ چیزیں دیکھتے ہیں۔
رینڈم کلاس سیوڈو بے ترتیب نمبر استعمال کرتی ہے۔ وہ کیسے؟ یہ پتہ چلتا ہے کہ بے ترتیب نمبر اتنے بے ترتیب نہیں ہیں؟
چھدم بے ترتیب پن java.util.Random
java.util.Random کلاس کے لیے دستاویزات میں کہا گیا ہے کہ اگر
رینڈم کی مثالیں ایک ہی
سیڈ پیرامیٹر کے ساتھ تخلیق کی جاتی ہیں اور مثالوں پر ایک ہی سلسلے کی کارروائیاں انجام دی جاتی ہیں، تو وہ اعداد کی ایک جیسی ترتیب واپس کرتے ہیں۔ اور اگر ہم قریب سے دیکھیں تو ہم دیکھ سکتے ہیں کہ
رینڈم میں دراصل
ایک کنسٹرکٹر ہے جو بیج کے طور پر کچھ
لمبی قیمت لیتا ہے:
Random rnd1 = new Random(1L);
Random rnd2 = new Random(1L);
boolean test = rnd1.nextInt(6) == rnd2.nextInt(6);
System.out.println("Test: " + test);
یہ مثال
درست ہو جائے گی کیونکہ
دونوں صورتوں کا بیج ایک ہی ہے۔ کیا کرنا ہے؟ ڈیفالٹ کنسٹرکٹر جزوی طور پر مسئلہ حل کرتا ہے۔
ذیل میں رینڈم کنسٹرکٹر کے مشمولات کی ایک مثال ہے ۔
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
ڈیفالٹ کنسٹرکٹر bitwise exclusive
OR آپریشن استعمال کرتا ہے ۔ اور موجودہ وقت کی نمائندگی کرنے والا
ایک طویل اور اس کے لیے کچھ
بیج استعمال کرتا ہے :
private static long seedUniquifier() {
for (;;) {
long current = seedUniquifier.get();
long next = current * 181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
Здесь интересно ещё и то, что каждый вызов метода получения
seedUniquifier изменяет meaning
seedUniquifier. То есть класс спроектирован так, чтобы максимально эффективно подбирать случайные числа. Однако, How и сказано в documentации, они "
are not cryptographically secure". То есть для Howих-то целей использования в криптографических целей (генерация паролей и т.п.) не годится, т.к. последовательность при должном подходе предсказывается. На эту тему есть в интернете примеры, например тут: "
Predicting the next Math.random() in Java". Или например исходный code тут: "
Vulnerability Weak Crypto". У
java.util.Random (генератора случайных чисел) есть некий "шорткат", то есть укороченная version вызова, которая выполняется через Math.random:
public static void main(String []args){
int random_number = 1 + (int) (Math.random() * 6);
System.out.println("Value: " + random_number);
}
Но если посмотреть внимательно, внутри сидит всё тот же Random:
public static double random() {
return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
private static final class RandomNumberGeneratorHolder {
static final Random randomNumberGenerator = new Random();
}
В JavaDoc советуют использовать класс
SecureRandom для "
cryptographically secure pseudo-random number generator".
Безопасный Random Java
Класс
SecureRandom является наследником
java.util.Random и расположен в пакете
java.security. Сравнение этих двух классов можно прочитать в статье "
Difference between java.util.Random and java.security.SecureRandom". Чем же так хорош этот SecureRandom? Дело в том, что для него источником случайных чисел является такая магически звучащая штука How "пул энтропии ядра". Это одновременно и плюс, и минус. Про минус этого можно прочитать в статье: "
The dangers of java.security.SecureRandom". Если кратко, в Linux есть генератор случайных чисел ядра (RNG). RNG генерирует случайные числа на основе данных из пула энтропии (entropy pool), который наполняется на основе случайных событий в системе, таких How тайминги клавиатуры и дисков, движения мыши, прерывания (interrupts), сетевой трафик. Подробнее про пул энтропии изложено в материале "
Случайные числа в Linux(RNG) or How «наполнить» /dev/random и /dev/urandom". На Windows системах используется SHA1PRNG, реализованная в sun.security.provider.SecureRandom. С развитием Java менялся и SecureRandom, о чём для полной картины стоит прочитать в обзоре "
Java SecureRandom updates as of April 2016".
Многопоточность or будь How Цезарь
Если смотреть code класса
Random, то вроде ничто не предвещает беды. Методы не помечены How
synchronized. Но есть одно НО: при создании
Random конструктором по умолчанию в нескольких потоках мы будем между ними делить один и тот же
instance seed, по которому будет создаваться
Random. А также при получении нового случайного числа у
instance так же меняется внутренний
AtomicLong. С одной стороны, в этом нет ничего страшного с логической точки зрения, т.к. используется
AtomicLong. С другой стороны, за всё надо платить, в том числе производительностью. И за это тоже. Поэтому даже в официальной documentации к
java.util.Random сказано: "
Instances of java.util.Random are threadsafe. However, the concurrent use of the same java.util.Random instance across threads may encounter contention and consequent poor performance. Consider instead using ThreadLocalRandom in multithreaded designs". То есть в многопоточных applicationsх при активном использовании
Random из нескольких потоков лучше использовать класс
ThreadLocalRandom. Его использование немного отличается от обычного
Random:
public static void main(String []args){
int rand = ThreadLocalRandom.current().nextInt(1,7);
System.out.println("Value: " + rand);
}
Как видим, для него мы не указываем
seed. Данный пример описан в официальном tutorial от Oracle:
Concurrent Random Numbers. Подробнее про данный класс можно прочитать в обзоре: "
Guide to ThreadLocalRandom in Java".
StreamAPI и Random
Благодаря выходу Java 8 у нас появилось много новых возможностей. В том числе и Stream API. И изменения коснулись и генерации
Random значений. Например, в классе
Random появorсь новые методы, которые позволяют получить
Stream со случайными значениями типа
int
,
double
or
long
. Например:
import java.util.Random;
public class HelloWorld{
public static void main(String []args){
new Random().ints(10, 1, 7).forEach(n -> System.out.println(n));
}
}
Также появился новый класс
SplittableRandom:
import java.util.SplittableRandom;
public class HelloWorld{
public static void main(String []args){
new SplittableRandom().ints(10, 1, 7).forEach(n -> System.out.println(n));
}
}
Подробнее про отличие
SplittableRandom от остальных классов можно прочитать здесь: "
Different ways to create Random numbers in Java".
Заключение
Думаю, стоит сделать вывод. Нужно внимательно читать JavaDoc к используемым классам. За такой простой на первый взгляд вещью How Random стоят нюансы, которые могут сыграть злую шутку. #Viacheslav
GO TO FULL VERSION