JavaRush /Blog Java /Random-MS /Teori kebarangkalian dalam amalan atau adakah anda tahu t...

Teori kebarangkalian dalam amalan atau adakah anda tahu tentang Random

Diterbitkan dalam kumpulan
Teori kebarangkalian dalam amalan atau adakah anda tahu tentang Random - 1

pengenalan

Terdapat banyak sains di dunia yang mengkaji teori kebarangkalian. Dan sains terdiri daripada pelbagai bahagian. Sebagai contoh, dalam matematik terdapat bahagian berasingan yang dikhaskan untuk mengkaji peristiwa rawak, kuantiti, dll. Tetapi sains tidak dipandang ringan. Dalam kes ini, teori kebarangkalian mula terbentuk apabila orang cuba memahami corak yang ada dalam membaling dadu apabila bermain permainan peluang. Jika anda melihat dengan teliti, terdapat banyak perkara yang kelihatan rawak di sekeliling kita. Tetapi segala-galanya secara rawak tidak sepenuhnya rawak. Tetapi lebih lanjut mengenai itu kemudian. Bahasa pengaturcaraan Java juga mempunyai sokongan untuk nombor rawak, bermula dengan versi pertama JDK. Nombor rawak dalam Java boleh digunakan menggunakan kelas java.util.Random . Untuk ujian, kami akan menggunakan pengkompil dalam talian tutorialspoint java . Berikut ialah contoh primitif menggunakan Rawak untuk meniru balingan "dadu", atau kiub dalam bahasa Rusia:
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);
    }
}
Nampaknya ini boleh menjadi penghujung perihalan Random , tetapi ia tidak semudah itu. Mari buka penerangan kelas java.util.Random dalam API Java. Dan di sini kita melihat perkara yang menarik. Kelas Rawak menggunakan nombor rawak pseudo. Bagaimana pula? Ternyata nombor rawak tidak begitu rawak?
Teori kebarangkalian dalam amalan atau adakah anda tahu tentang Random - 2

Pseudo-randomness java.util.Random

Dokumentasi untuk kelas java.util.Random mengatakan bahawa jika kejadian Rawak dicipta dengan parameter benih yang sama dan urutan tindakan yang sama dilakukan pada kejadian, ia mengembalikan urutan nombor yang sama. Dan jika kita melihat dengan teliti, kita dapat melihat bahawa Random sebenarnya mempunyai pembina yang mengambil nilai yang lama sebagai benih:
Random rnd1 = new Random(1L);
Random rnd2 = new Random(1L);
boolean test = rnd1.nextInt(6) == rnd2.nextInt(6);
System.out.println("Test: " + test);
Contoh ini akan kembali benar kerana benih kedua-dua kejadian adalah sama. Apa nak buat? Pembina lalai sebahagiannya menyelesaikan masalah. Di bawah ialah contoh kandungan pembina Rawak :
public Random() {
	this(seedUniquifier() ^ System.nanoTime());
}
Pembina lalai menggunakan operasi bitwise exclusive OR . Dan menggunakan panjang mewakili masa semasa dan beberapa benih untuk ini :
private static long seedUniquifier() {
	for (;;) {
		long current = seedUniquifier.get();
		long next = current * 181783497276652981L;
		if (seedUniquifier.compareAndSet(current, next))
			return next;
	}
}
Satu lagi perkara yang menarik di sini ialah setiap panggilan ke kaedah getter seedUniquifier mengubah nilai seedUniquifier . Iaitu, kelas direka bentuk untuk memilih nombor rawak secekap mungkin. Walau bagaimanapun, seperti yang dinyatakan dalam dokumentasi, mereka " tidak selamat dari segi kriptografi ". Iaitu, untuk beberapa tujuan penggunaan untuk tujuan kriptografi (penjanaan kata laluan, dll.) ia tidak sesuai, kerana urutan dengan pendekatan yang betul diramalkan. Terdapat contoh mengenai topik ini di Internet, contohnya di sini: " Meramalkan Math.random() seterusnya dalam Java ". Atau sebagai contoh kod sumber di sini: " Vulnerability Weak Crypto ". java.util.Random (penjana nombor rawak) mempunyai "pintasan" tertentu, iaitu versi pendek panggilan yang dilaksanakan melalui Math.random:
public static void main(String []args){
	int random_number = 1 + (int) (Math.random() * 6);
	System.out.println("Value: " + random_number);
}
Tetapi jika anda melihat dengan teliti, Random yang sama ada di dalam:
public static double random() {
	return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
private static final class RandomNumberGeneratorHolder {
	static final Random randomNumberGenerator = new Random();
}
JavaDoc menasihatkan menggunakan kelas SecureRandom untuk " penjana nombor pseudo-rawak yang selamat secara kriptografi ".
Teori kebarangkalian dalam amalan atau adakah anda tahu tentang Random - 3

Java Rawak Selamat

Kelas SecureRandom ialah subkelas java.util.Random dan terletak dalam pakej java.security . Perbandingan kedua-dua kelas ini boleh dibaca dalam artikel " Perbezaan antara java.util.Random dan java.security.SecureRandom ". Mengapa SecureRandom ini sangat bagus? Hakikatnya ialah baginya sumber nombor rawak adalah sesuatu yang ajaib seperti "kolam entropi teras". Ini adalah tambah dan tolak. Anda boleh membaca tentang keburukan ini dalam artikel: “ Bahaya java.security.SecureRandom ”. Ringkasnya, Linux mempunyai penjana nombor rawak kernel (RNG). RNG menjana nombor rawak berdasarkan data daripada kumpulan entropi, yang diisi berdasarkan peristiwa rawak dalam sistem, seperti pemasaan papan kekunci dan cakera, pergerakan tetikus, gangguan dan trafik rangkaian. Maklumat lanjut tentang kumpulan entropi diterangkan dalam bahan " Nombor rawak dalam Linux (RNG) atau cara “mengisi” /dev/random dan /dev/urandom ". Pada sistem Windows, SHA1PRNG digunakan, dilaksanakan dalam sun.security.provider.SecureRandom. Dengan pembangunan Java, SecureRandom juga berubah, yang patut dibaca dalam ulasan " Kemas kini Java SecureRandom pada April 2016 " untuk gambaran lengkap.
Teori kebarangkalian dalam amalan atau adakah anda tahu tentang Random - 4

Multithreading atau jadi seperti Caesar

Jika anda melihat kod kelas Random , nampaknya tiada apa-apa yang menunjukkan masalah. Kaedah tidak ditandakan disegerakkan . Tetapi ada satu TETAPI: apabila mencipta Rawak dengan pembina lalai dalam beberapa utas, kami akan berkongsi benih contoh yang sama antara mereka , yang mana Rawak akan dibuat . Dan juga apabila nombor rawak baharu diterima, AtomicLong dalaman contoh turut berubah . Di satu pihak, tidak ada yang salah dengan ini dari sudut pandangan logik, kerana... AtomicLong digunakan . Sebaliknya, anda perlu membayar untuk segala-galanya, termasuk produktiviti. Dan untuk ini juga. Oleh itu, walaupun dokumentasi rasmi untuk java.util.Random berkata: " Instance java.util.Random adalah threadsafe. Walau bagaimanapun, penggunaan serentak java.util.Random yang sama merentas urutan mungkin menghadapi perbalahan dan akibatnya prestasi yang lemah. Pertimbangkan sebaliknya menggunakan ThreadLocalRandom dalam reka bentuk berbilang benang ". Iaitu, dalam aplikasi berbilang benang apabila secara aktif menggunakan Rawak daripada beberapa utas, lebih baik menggunakan kelas ThreadLocalRandom . Penggunaannya sedikit berbeza daripada Random biasa :
public static void main(String []args){
	int rand = ThreadLocalRandom.current().nextInt(1,7);
	System.out.println("Value: " + rand);
}
Seperti yang anda lihat, kami tidak menentukan benih untuknya . Contoh ini diterangkan dalam tutorial rasmi daripada Oracle: Concurrent Random Numbers . Anda boleh membaca lebih lanjut mengenai kelas ini dalam ulasan: " Panduan untuk ThreadLocalRandom di Java ".
Teori kebarangkalian dalam amalan atau adakah anda tahu tentang Random - 5

StreamAPI dan Rawak

Dengan keluaran Java 8, kami mempunyai banyak ciri baharu. Termasuk Stream API. Dan perubahan juga mempengaruhi penjanaan nilai Rawak . Sebagai contoh, kelas Rawak mempunyai kaedah baharu yang membolehkan anda mendapatkan Strim dengan nilai rawak seperti int, doubleatau long. Sebagai contoh:
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));
    }
}
Terdapat juga kelas baru 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));
    }
}
Anda boleh membaca lebih lanjut tentang perbezaan antara SplittableRandom dan kelas lain di sini: " Cara yang berbeza untuk mencipta nombor Rawak dalam Java ".

Kesimpulan

Saya fikir ia berbaloi untuk membuat kesimpulan. Anda perlu membaca JavaDoc dengan teliti untuk kelas yang digunakan. Di sebalik sesuatu yang mudah pada pandangan pertama seperti Random terdapat nuansa yang boleh memainkan jenaka yang kejam. #Viacheslav
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION