JavaRush /Java-Blog /Random-DE /Wahrscheinlichkeitstheorie in der Praxis oder kennen Sie ...
Viacheslav
Level 3

Wahrscheinlichkeitstheorie in der Praxis oder kennen Sie sich mit Zufall aus?

Veröffentlicht in der Gruppe Random-DE
Wahrscheinlichkeitstheorie in der Praxis oder kennen Sie sich mit Zufall aus - 1

Einführung

Es gibt viele Wissenschaften auf der Welt, die sich mit der Wahrscheinlichkeitstheorie befassen. Und die Wissenschaften bestehen aus verschiedenen Abschnitten. In der Mathematik gibt es beispielsweise einen eigenen Abschnitt, der sich mit der Untersuchung zufälliger Ereignisse, Mengen usw. befasst. Aber die Wissenschaft wird nicht auf die leichte Schulter genommen. In diesem Fall nahm die Wahrscheinlichkeitstheorie Gestalt an, als man versuchte zu verstehen, welche Muster es beim Würfeln bei Glücksspielen gab. Wenn man genau hinschaut, gibt es um uns herum viele scheinbar zufällige Dinge. Aber alles Zufällige ist nicht völlig zufällig. Aber dazu später mehr. Die Programmiersprache Java unterstützt seit der ersten Version des JDK auch Zufallszahlen. Zufallszahlen in Java können mit der Klasse java.util.Random verwendet werden . Zum Testen verwenden wir den Tutorialspoint Java Online Compiler . Hier ist ein einfaches Beispiel für die Verwendung von Random , um das Werfen von „Würfeln“ oder Würfeln auf Russisch zu emulieren:
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);
    }
}
Es scheint, dass dies das Ende der Beschreibung von Random sein könnte , aber so einfach ist es nicht. Öffnen wir die Beschreibung der Klasse java.util.Random in der Java-API. Und hier sehen wir interessante Dinge. Die Random- Klasse verwendet Pseudozufallszahlen. Wie so? Es stellt sich heraus, dass Zufallszahlen nicht so zufällig sind?
Wahrscheinlichkeitstheorie in der Praxis oder kennen Sie sich mit Zufall aus - 2

Pseudozufälligkeit java.util.Random

Die Dokumentation für die Klasse java.util.Random besagt, dass, wenn Instanzen von Random mit demselben Startparameter erstellt werden und dieselben Aktionssequenzen für die Instanzen ausgeführt werden, sie identische Zahlensequenzen zurückgeben. Und wenn wir genau hinschauen, können wir erkennen, dass Random tatsächlich einen Konstruktor hat, der einen Long- Wert als Startwert akzeptiert :
Random rnd1 = new Random(1L);
Random rnd2 = new Random(1L);
boolean test = rnd1.nextInt(6) == rnd2.nextInt(6);
System.out.println("Test: " + test);
Dieses Beispiel wird true zurückgeben, weil Der Samen beider Instanzen ist derselbe. Was zu tun? Der Standardkonstruktor löst das Problem teilweise. Nachfolgend finden Sie ein Beispiel für den Inhalt des Random- Konstruktors :
public Random() {
	this(seedUniquifier() ^ System.nanoTime());
}
Der Standardkonstruktor verwendet die bitweise Exklusiv -ODER- Operation . Und verwendet dafür ein long , das die aktuelle Zeit darstellt, und etwas Startwert :
private static long seedUniquifier() {
	for (;;) {
		long current = seedUniquifier.get();
		long next = current * 181783497276652981L;
		if (seedUniquifier.compareAndSet(current, next))
			return next;
	}
}
Eine weitere interessante Sache hier ist, dass jeder Aufruf der getter-Methode „seedUniquifier “ den Wert von „seedUniquifier“ ändert . Das heißt, die Klasse ist darauf ausgelegt, Zufallszahlen so effizient wie möglich auszuwählen. Allerdings sind sie, wie es in der Dokumentation heißt, „nicht kryptografisch sicher “. Das heißt, für einige Verwendungszwecke für kryptografische Zwecke (Passwortgenerierung usw.) ist es nicht geeignet, weil Die Reihenfolge mit dem richtigen Ansatz wird vorhergesagt. Beispiele zu diesem Thema gibt es im Internet, zum Beispiel hier: „ Predicting the next Math.random() in Java “. Oder zum Beispiel den Quellcode hier: „ Vulnerability Weak Crypto “. java.util.Random (Zufallszahlengenerator) hat eine bestimmte „Abkürzung“, also eine verkürzte Version des Aufrufs, der über Math.random ausgeführt wird:
public static void main(String []args){
	int random_number = 1 + (int) (Math.random() * 6);
	System.out.println("Value: " + random_number);
}
Aber wenn man genau hinschaut, sitzt darin derselbe Zufall:
public static double random() {
	return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
private static final class RandomNumberGeneratorHolder {
	static final Random randomNumberGenerator = new Random();
}
Das JavaDoc empfiehlt die Verwendung der SecureRandom- Klasse für einen „ kryptografisch sicheren Pseudozufallszahlengenerator “.
Wahrscheinlichkeitstheorie in der Praxis oder kennen Sie sich mit Zufall aus - 3

Sicheres zufälliges Java

Die SecureRandom- Klasse ist eine Unterklasse von java.util.Random und befindet sich im Paket java.security . Ein Vergleich dieser beiden Klassen kann im Artikel „ Unterschied zwischen java.util.Random und java.security.SecureRandom “ nachgelesen werden. Warum ist dieses SecureRandom so gut? Tatsache ist, dass für ihn die Quelle der Zufallszahlen ein so magisch klingendes Ding wie der „Kern-Entropie-Pool“ ist. Das ist sowohl ein Plus als auch ein Minus. Welche Nachteile dies mit sich bringt, können Sie im Artikel „ Die Gefahren von java.security.SecureRandom “ nachlesen. Kurz gesagt, Linux verfügt über einen Kernel-Zufallszahlengenerator (RNG). RNG generiert Zufallszahlen auf der Grundlage von Daten aus dem Entropiepool, der auf der Grundlage zufälliger Ereignisse im System wie Tastatur- und Festplatten-Timings, Mausbewegungen, Interrupts und Netzwerkverkehr gefüllt wird. Weitere Informationen zum Entropiepool finden Sie im Material „ Zufallszahlen in Linux (RNG) oder wie man /dev/random und /dev/urandom „füllt“ . Auf Windows-Systemen wird SHA1PRNG verwendet, implementiert in sun.security.provider.SecureRandom. Mit der Entwicklung von Java hat sich auch SecureRandom verändert, wofür es sich lohnt, im Testbericht „ Java SecureRandom-Updates ab April 2016 “ nachzulesen, um sich ein vollständiges Bild zu machen.
Wahrscheinlichkeitstheorie in der Praxis oder kennen Sie sich mit Zufall aus - 4

Multithreading oder sei wie Caesar

Wenn Sie sich den Code der Random- Klasse ansehen , scheint nichts auf Probleme hinzuweisen. Methoden sind nicht als synchronisiert markiert . Aber es gibt ein ABER: Wenn wir Random mit dem Standardkonstruktor in mehreren Threads erstellen, teilen wir uns den gleichen Instanz-Seed , wodurch Random erstellt wird . Und wenn eine neue Zufallszahl empfangen wird, ändert sich auch die interne AtomicLong der Instanz . Einerseits ist daran aus logischer Sicht nichts auszusetzen, denn... AtomicLong wird verwendet . Andererseits muss man für alles bezahlen, auch für die Produktivität. Und auch dafür. Daher heißt es sogar in der offiziellen Dokumentation für java.util.Random : „ Instanzen von java.util.Random sind threadsicher. Die gleichzeitige Verwendung derselben java.util.Random-Instanz über Threads hinweg kann jedoch zu Konflikten und daraus resultierender schlechter Leistung führen. Bedenken Sie.“ Verwenden Sie stattdessen ThreadLocalRandom in Multithread-Designs . Das heißt, in Multithread-Anwendungen ist es besser, die ThreadLocalRandom- Klasse zu verwenden, wenn Random aus mehreren Threads aktiv verwendet wird . Seine Verwendung unterscheidet sich geringfügig von der regulären Random :
public static void main(String []args){
	int rand = ThreadLocalRandom.current().nextInt(1,7);
	System.out.println("Value: " + rand);
}
Wie Sie sehen, geben wir dafür keinen Seed an . Dieses Beispiel wird im offiziellen Tutorial von Oracle: Concurrent Random Numbers beschrieben . Weitere Informationen zu dieser Klasse finden Sie in der Rezension: „ Guide to ThreadLocalRandom in Java “.
Wahrscheinlichkeitstheorie in der Praxis oder kennen Sie sich mit Zufall aus - 5

StreamAPI und Random

Mit der Veröffentlichung von Java 8 haben wir viele neue Funktionen. Einschließlich Stream-API. Und die Änderungen wirkten sich auch auf die Generierung von Zufallswerten aus . Die Random- Klasse verfügt beispielsweise über neue Methoden, mit denen Sie einen Stream mit Zufallswerten wie int, doubleoder abrufen können long. Zum Beispiel:
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));
    }
}
Es gibt auch eine neue Klasse 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));
    }
}
Mehr über den Unterschied zwischen SplittableRandom und anderen Klassen können Sie hier lesen: „ Verschiedene Möglichkeiten, Zufallszahlen in Java zu erstellen “.

Abschluss

Ich denke, es lohnt sich, ein Fazit zu ziehen. Sie müssen das JavaDoc für die verwendeten Klassen sorgfältig lesen. Hinter etwas, das auf den ersten Blick so einfach ist wie Random, stecken Nuancen, die einen grausamen Witz spielen können. #Wjatscheslaw
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION