In Bezug darauf rand() % n
, weniger als ideal zu sein
Tun rand() % n
hat eine ungleichmäßige Verteilung. Sie erhalten eine unverhältnismäßige Anzahl bestimmter Werte, da die Anzahl der Werte kein Vielfaches von 20 ist
Als nächstes rand()
wird typischerweise ein linearer Kongruenzgenerator verwendet (es gibt viele andere , nur dies ist der wahrscheinlichste implementierte - und mit weniger als idealen Parametern (es gibt viele Möglichkeiten, die Parameter auszuwählen)). Das größte Problem dabei ist, dass die niedrigen Bits darin (die, die Sie mit einem % 20
Typausdruck erhalten) oft nicht so zufällig sind. Ich erinnere mich an eine rand()
von Jahren , wo das niedrigste Bit von abgewechselt , 1
um 0
mit jedem Anruf rand()
- es ist nicht sehr zufällig war.
Aus der Manpage von rand (3):
Die Versionen von rand () und srand () in der Linux C-Bibliothek verwenden dasselbe
Zufallszahlengenerator als random () und srandom (), also die niedrigere Ordnung
Bits sollten so zufällig sein wie die Bits höherer Ordnung. Allerdings bei älteren
rand () - Implementierungen und aktuelle Implementierungen auf verschiedenen
Systeme sind die Bits niedrigerer Ordnung viel weniger zufällig als die Bits höherer Ordnung
Bits bestellen. Verwenden Sie diese Funktion nicht in Anwendungen, für die dies vorgesehen ist
tragbar, wenn gute Zufälligkeit benötigt wird.
Dies mag jetzt in die Geschichte verbannt sein, aber es ist durchaus möglich, dass Sie immer noch eine schlechte rand () - Implementierung haben, die sich irgendwo im Stapel versteckt. In diesem Fall ist es immer noch durchaus anwendbar.
Die Sache zu tun ist, tatsächlich eine gute Zufallszahlenbibliothek zu verwenden (die gute Zufallszahlen liefert) und dann nach Zufallszahlen innerhalb des gewünschten Bereichs zu fragen.
Ein Beispiel für ein gutes Zufallszahlen-Codebit (ab 13:00 im verknüpften Video)
#include <iostream>
#include <random>
int main() {
std::mt19937 mt(1729); // yes, this is a fixed seed
std::uniform_int_distribution<int> dist(0, 99);
for (int i = 0; i < 10000; i++) {
std::cout << dist(mt) << " ";
}
std::cout << std::endl;
}
Vergleichen Sie dies mit:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
srand(time(NULL));
for (int i = 0; i < 10000; i++) {
printf("%d ", rand() % 100);
}
printf("\n");
}
Führen Sie beide Programme aus und vergleichen Sie, wie oft bestimmte Zahlen in dieser Ausgabe angezeigt werden (oder nicht).
In Verbindung stehendes Video: rand () als schädlich angesehen
Einige historische Aspekte von rand (), die Fehler in Nethack verursachen, die man bei seinen eigenen Implementierungen beobachten und berücksichtigen sollte:
Nethack RNG Problem
Rand () ist eine sehr grundlegende Funktion für die Zufallszahlengenerierung von Nethack. Die Art und Weise, wie Nethack es benutzt, ist fehlerhaft oder es kann argumentiert werden, dass lrand48 () beschissene Pseudozufallszahlen erzeugt. (Lrand48 () ist jedoch eine Bibliotheksfunktion, die eine definierte PRNG-Methode verwendet, und jedes Programm, das sie verwendet, sollte die Schwächen dieser Methode berücksichtigen.)
Der Fehler ist, dass Nethack (manchmal ausschließlich wie in rn (2)) auf die unteren Bits der Ergebnisse von lrand48 () angewiesen ist. Aus diesem Grund funktioniert RNG im gesamten Spiel schlecht. Dies macht sich insbesondere dann bemerkbar, wenn Benutzeraktionen weitere Zufälligkeiten einführen, dh bei der Charaktererstellung und der Erstellung der ersten Ebene.
Obwohl das oben Genannte aus dem Jahr 2003 stammt, sollte es dennoch beachtet werden, da möglicherweise nicht alle Systeme, auf denen Ihr beabsichtigtes Spiel ausgeführt wird, ein aktuelles Linux-System mit einer guten rand () -Funktion sind.
Wenn Sie dies nur für sich selbst tun, können Sie testen, wie gut Ihr Zufallszahlengenerator ist, indem Sie Code schreiben und die Ausgabe mit ent testen .
Über die Eigenschaften von Zufallszahlen
Es gibt andere Interpretationen von "zufällig", die nicht genau zufällig sind. In einem zufälligen Datenstrom ist es durchaus möglich, dieselbe Zahl zweimal zu erhalten. Wenn Sie eine Münze werfen (zufällig), ist es durchaus möglich, zwei Köpfe hintereinander zu bekommen. Oder wirf zweimal einen Würfel und erhalte zweimal hintereinander dieselbe Zahl. Oder ein Roulette-Rad drehen und dort zweimal die gleiche Nummer bekommen.
Die Verteilung von Zahlen
Beim Abspielen einer Songliste erwarten die Leute, dass "zufällig" bedeutet, dass derselbe Song oder Künstler nicht ein zweites Mal hintereinander gespielt wird. Eine Wiedergabeliste mit den Beatles zweimal hintereinander spielen wird als ‚nicht zufällig‘ zu haben (obwohl es ist zufällig). Die Wahrnehmung, dass für eine Wiedergabeliste von vier Songs insgesamt acht Mal gespielt wurde:
1 3 2 4 1 2 4 3
ist eher 'zufällig' als:
1 3 3 2 1 4 4 2
Mehr dazu zum 'Mischen' von Songs: Wie mische ich Songs?
Bei wiederholten Werten
Wenn Sie Werte nicht wiederholen möchten, sollten Sie einen anderen Ansatz in Betracht ziehen. Generieren Sie alle möglichen Werte und mischen Sie sie.
Wenn Sie anrufen rand()
(oder einen anderen Zufallszahlengenerator), rufen Sie ihn mit Ersatz an. Sie können immer zweimal dieselbe Nummer erhalten. Eine Möglichkeit besteht darin, die Werte immer wieder herauszuwerfen, bis Sie einen auswählen, der Ihren Anforderungen entspricht. Ich werde darauf hinweisen, dass dies eine nicht deterministische Laufzeit hat und es möglich ist, dass Sie sich in einer Situation befinden, in der es eine Endlosschleife gibt, wenn Sie nicht mit einer komplexeren Rückverfolgung beginnen.
Liste und Auswahl
Eine andere Möglichkeit besteht darin, eine Liste aller möglichen gültigen Zustände zu erstellen und dann ein zufälliges Element aus dieser Liste auszuwählen. Finde alle leeren Stellen (die einigen Regeln entsprechen) im Raum und wähle dann eine zufällige aus dieser Liste aus. Und dann mach es immer wieder, bis du fertig bist.
Mischen
Der andere Ansatz besteht darin, zu mischen, als wäre es ein Kartenspiel. Beginnen Sie mit allen leeren Stellen im Raum und weisen Sie sie dann zu, indem Sie die leeren Stellen nacheinander jeder Regel / jedem Prozess zuordnen und nach einer leeren Stelle fragen. Sie sind fertig, wenn Ihnen die Karten ausgehen oder die Dinge nicht mehr nach ihnen fragen.