Ich arbeite derzeit an einem Programm, das zufälliges Rauschen auf einem Bildschirm basierend auf den 'Koordinaten' eines Pixels erzeugen soll. Die Koordinaten sollten bei jedem Neustart des Programms dieselbe Farbe haben. Wenn ich jedoch das util.Random von Java verwende, sind die Ergebnisse, die ich erhalte, nicht so zufällig, wie ich es gerne hätte:
Ich dachte, wenn ich die kombinierten Koordinaten verwenden würde (wie in einer ganzen Zahl, die aus beiden Koordinaten nebeneinander gebildet wird), hätte jede Koordinate eine andere Nummer. Indem ich diese Zahl als Startwert verwendete, erwartete ich, dass ich für jede Koordinate eine andere Zufallszahl für den RGB-Wert dieser Koordinate erhalten würde.
Dies ist der Code, den ich verwendet habe:
public class Generate {
static Random Random;
public static int TileColor(int x, int y){
Random = new Random(Integer.valueOf(Integer.toString(x)+Integer.toString(y)));
int b = 1 + Random.nextInt(50);
int g = 1 + Random.nextInt(50);
int r = 1 + Random.nextInt(50);
int color = -Color.rgb888(r, g, b);
return color;
}
}
Liegt das Muster, das das Programm erstellt, an der Funktionsweise der Zufallsfunktion von Java, oder mache ich etwas falsch und sollte ich einen anderen Ansatz wählen?
Update: Ich habe jetzt versucht, die Probleme mit der Verkettung mithilfe des folgenden Codes zu beseitigen:
public static int TileColor(int x, int y){
Randomy = new Random(y);
Randomx = new Random(x);
Random = new Random(Integer.valueOf(Integer.toString(Randomx.nextInt(1234))+Integer.toString(Randomy.nextInt(1234))));
int b = 1 + Random.nextInt(100);
int g = 1 + Random.nextInt(100);
int r = 1 + Random.nextInt(100);
int color = -Color.rgb888(r, g, b);
return color;
}
Irgendwie lieferte dies auch ein (meiner Meinung nach) ausreichend zufälliges Bild:
Dieser Code wird jedoch dreimal pro Pixel neu berechnet. Obwohl dies für mich derzeit kein Problem ist, überlege ich mir, diesen Code zu ändern, falls ich später eine bessere Leistung benötige.
Antworten:
In Javas
java.util.Random
Klasse erhalten Sie normalerweise Folgen von Pseudozufallszahlen, die für die Verwendung in Spiel 1 ausreichen . Dieses Merkmal gilt jedoch nur für eine Folge von mehreren Proben, die auf einem Samen basieren. Wenn Sie das RNG mit inkrementierenden Startwerten neu initialisieren und nur den ersten Wert jeder Sequenz betrachten, sind die Zufälligkeitseigenschaften bei weitem nicht so gut.Was Sie stattdessen tun könnten:
Verwenden Sie anstelle eines Zufallszahlengenerators eine Nachrichtenauszugsfunktion , um ein Koordinatenpaar in einen Farbwert umzuwandeln. Die Ausgabe der meisten MDFs ist nicht vorhersehbar genug, um die meisten Zufallstests zu erfüllen. Die Ausgabe ist normalerweise mehr als die 24 Bit, die Sie für einen RGB-Wert benötigen, aber das Abschneiden ist normalerweise kein Problem.
Um die Leistung zu verbessern, können Sie die Generierung von Nachrichtenübersichten mit Blöcken kombinieren. Generieren Sie kleine Pixelblöcke, die gerade groß genug sind, um die gesamte Länge einer Ausgabe Ihrer Digest-Funktion zu nutzen.
1 Wenn es unbedingt erforderlich ist, dass niemand die nächste Zahl vorhersagen kann, verwenden Sie die langsamere, aber weniger vorhersagbare
java.security.SecureRandom
quelle
In diesem Fall sollten Sie eine deterministische Rauschfunktion wie Perlin-Rauschen oder Simplex-Rauschen verwenden .
( Weitere Informationen zu Perlin-Rauschen mit einigen schönen Bildern finden Sie in dieser Frage. )
Die Verwendung einer eingebauten
random()
oder einer ähnlichen Funktion gibt Ihnen zum größten Teil jedes Mal, wenn Sie das Programm ausführen, andere Werte, da sie die Uhr als Eingabe oder einen anderen Pseudozufallswert verwenden können.Eine andere Möglichkeit ist, eine "Noise Map" einmal offline zu erstellen und diese dann später als Zufallszahlenquelle zu verwenden.
In Ihrer Implementierung verketten Sie die Zeichenfolgendarstellungen von x und
y
. Das ist schlecht, da es nicht domänenweit eindeutig ist. Zum Beispiel,Viel Glück!
quelle
Mal sehen, was Sie genau tun:
Das klingt alles in Ordnung, aber Sie erhalten ein Muster, weil:
Pixel bei 1,11 und Pixel bei 11,1 haben beide die Nummer 111, sodass sie mit Sicherheit dieselbe Farbe haben.
Solange Sie immer auf die gleiche Weise fahren, können Sie nur einen Generator verwenden, ohne dass Sie einen für jedes Pixel verwenden müssen. Eine für das ganze Bild reicht! Wegen der Pseudozufälligkeit wird es immer noch einige Muster geben. @David_Lively verwendet einen Noise-Algorithmus, der zufällig aussieht.
quelle
Machen Sie einen Farbgenerator und produzieren Sie dann Ihre Farben für Ihre Fliese. Nur einmal säen! Sie müssen nicht mehr als das säen, zumindest nicht pro Fliese.
Und die Verwendung wird wie folgt sein:
Wenn Sie mit dem Ergebnis nicht zufrieden sind, ändern Sie einfach den
Random
Samen. Außerdem müssen Sie nur den Startwert und die Größen speichern / mitteilen, damit alle Kunden dasselbe Image haben.quelle
Anstatt Random zu verwenden, sollten Sie einen Hash-Digest wie MD5 verwenden. Es bietet einen schwer vorhersagbaren 'zufälligen' Wert, der auf einer bestimmten Eingabe basiert, aber immer den gleichen Wert für dieselbe Eingabe.
Beispiel:
HINWEIS: Ich weiß nicht, woher Color.rgb888 (..) kommt, daher weiß ich nicht, welcher Bereich zulässig ist. 0-255 ist jedoch normal.
Zu berücksichtigende Verbesserungen:
quelle
Andere haben darauf hingewiesen, dass eine Möglichkeit, das gewünschte Verhalten zu erzielen, die Verwendung einer Hash-Funktion, auch "Message Digest-Funktion" genannt, ist. Das Problem ist, dass diese häufig auf Algorithmen wie MD5 basieren, die kryptografisch sicher (dh wirklich, wirklich, wirklich zufällig), aber sehr langsam sind. Wenn Sie jedes Mal, wenn Sie ein zufälliges Pixel benötigen, eine kryptografische Hash-Funktion verwenden, treten erhebliche Leistungsprobleme auf.
Es gibt jedoch nicht-kryptografische Hash-Funktionen, die Werte erzeugen können, die für Ihren Zweck zufällig und gleichzeitig schnell sind. Das, wonach ich normalerweise greife, ist Murmeln . Ich bin kein Java-Benutzer, aber es scheint, dass mindestens eine Java-Implementierung verfügbar ist. Wenn Sie feststellen, dass wirklich jedes Pixel anhand seiner Koordinaten generiert werden muss, anstatt sie alle auf einmal zu generieren und in einer Textur zu speichern, ist dies eine gute Möglichkeit, dies zu tun.
quelle
Ich würde eine Primzahl über 2000 verwenden (maximale typische Auflösung).
Dies minimiert (oder eliminiert doppelte Samen).
quelle
Random
ist zufällig genug. Sie verwenden es aus zwei Hauptgründen falsch.Integer.valueOf(Integer.toString(x)+Integer.toString(y))
Zwischen den Pixeln, mit denen Sie arbeiten, besteht eine enorme Korrelation .Ich würde nur eine Variante des folgenden Codes verwenden, in der Sie eine Hash-Funktion (verwenden Sie nicht Integer.getHashCode) aus den Antworten unter /programming/9624963/java-simplest-integer- auswählen können. hash
wo die Hash-Funktion sein könnte
quelle
Sie können versuchen, die aktuelle Uhrzeit des Systems als Startwert zu verwenden:
Hoffentlich ergibt sich ein zufälligerer Wert.
quelle
Hier ist eine einzeilige statische Shader-Funktion, die ich mir ausgedacht habe - Poltergeist (Noisy Ghost).
Es benötigt eine 2D-Koordinate und einen Startwert und wird wie gewünscht monoton wiedergegeben. Es läuft in Echtzeit mit fps, unabhängig von der Bildschirmauflösung. Dafür sind GPUs da.
Jede Auflösung, jede Textur, auf jedem Gerät (auch auf Mobilgeräten), das GL unterstützt (was mit einem Bildschirm so ziemlich alles ist).
Sieh es hier laufen, gerade jetzt!
https://www.shadertoy.com/view/ltB3zD
Sie können diesen Shader einfach in Ihr Java-Programm mit Standard-OpenGL oder in jeden Browser mit Standard-WebGL einbinden.
Aus Spaß schmeiße ich den Spießrutenlauf, damit jeder Poltergeist in Qualität und Leistung auf allen Geräten schlagen kann. Noisy Ghost-Regeln! Unbesiegt!
quelle