Warum Ihr Zufall vorhersehbar ist: Zufallszahlen in Go
Min-jun Kim
Dev Intern · Leapcell

Einführung
Zufallszahlen werden in der Informatik häufig verwendet, von der Kryptographie bis hin zu Simulationen und Spielen. Sie lassen sich in zwei Arten einteilen: echte Zufallszahlen und Pseudozufallszahlen.
Echte Zufallszahlen
Echte Zufallszahlen werden mit Hilfe physikalischer Phänomene erzeugt, wie z. B. Münzwurf, Würfeln, Drehen von Rädern, elektronisches Rauschen, Kernspaltung und mehr. Zufallszahlengeneratoren, die auf diesen Methoden basieren, werden als physikalische Zufallszahlengeneratoren bezeichnet.
Pseudozufallszahlen
Pseudozufall bedeutet einen Prozess, der zufällig erscheint, aber nicht zufällig ist. Pseudozufallszahlen werden beispielsweise mit deterministischen Algorithmen berechnet, um Sequenzen zu erzeugen, die zufällig erscheinen.
Funktionen, die zur Berechnung von Pseudozufallszahlen verwendet werden, werden als Zufallsfunktionen bezeichnet, und Algorithmen, die Zufallsfunktionen zur Erzeugung von Zufallszahlen verwenden, werden als Zufallszahlengeneratoren bezeichnet. Einige Zufallsfunktionen sind periodisch. Nicht-periodische Funktionen sind im Allgemeinen besser, aber periodische Zufallsfunktionen sind oft schneller. Einige periodische Funktionen haben einstellbare Koeffizienten, die es ihnen ermöglichen, sehr große Perioden zu erreichen, wodurch sie fast so effektiv sind wie nicht-periodische Funktionen.
Werfen wir einen Blick auf die Pseudozufalls-Implementierung in Golang.
Zufallszahlen in frühen Go-Versionen
// Go 1.18 import ( "fmt" "math/rand" ) func main() { fmt.Println(rand.Intn(100)) }
Wenn dieser Code ausgeführt wird, ist das Ergebnis immer dasselbe, egal wie oft Sie ihn ausführen. Warum? Keine Panik - lassen Sie uns die Implementierung untersuchen. Wenn wir den Quellcode untersuchen, können wir sehen, dass die Bibliothek math/rand
eine Standardquelle mit einem Startwert von 1 verwendet, um Zufallszahlen zu erzeugen. Wie bereits erwähnt, sind Pseudozufallszahlen nicht wirklich zufällig, da sie von deterministischen Algorithmen erzeugt werden. Wenn der Seed in NewSource
unverändert bleibt, ist die Folge von Zufallszahlen bei jedem Neustart des Programms gleich.
Quellcode: rand.go (Go 1.18)
// ... /* * Top-Level Convenience Functions */ var globalRand = New(&lockedSource{src: NewSource(1).(*rngSource)}) // ...
Um sicherzustellen, dass der Seed bei jeder Ausführung unterschiedlich ist, können Sie einen Zeitstempel als Seed verwenden. Dadurch wird sichergestellt, dass sich die Zufallssequenz bei jeder Programmausführung ändert.
// Go 1.18 package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) fmt.Println(rand.Intn(100)) // Das Ergebnis ist jedes Mal anders, wenn Sie das Programm ausführen }
Glücklicherweise hat Google dieses Problem in Go 1.20 behoben. Ab Go 1.20 wird der Zufalls-Seed für den globalen Zufallszahlengenerator automatisch initialisiert und nicht mehr auf 1 festgelegt. Sofern die Umgebungsvariable GODEBUG=randautoseed=0
nicht gesetzt ist, ändert sich die Zufallssequenz bei jeder Programmausführung.
Kryptografisch sichere Zufallszahlen
Die Bibliothek crypto/rand
implementiert einen sichereren Zufallszahlengenerator. Laut den Code-Kommentaren priorisiert sie auf Linux-basierten Plattformen den Systemaufruf getrandom(2)
. Wenn dieser nicht verfügbar ist, greift sie auf /dev/urandom
zurück.
getrandom
ist ein Systemaufruf, der das Lesen aus der Gerätedatei /dev/urandom
kapselt, um hochwertige Zufallszahlen zu erhalten. /dev/urandom
verwendet /dev/random
als Seed-Referenz, und /dev/random
leitet seine Werte von hardwaregeneriertem Rauschen ab, das eine sehr hohe Zufallsqualität aufweist.
package rand import "io" // Reader ist eine globale, gemeinsam genutzte Instanz eines kryptografisch // sicheren Zufallszahlengenerators. // // Auf Linux, FreeBSD, Dragonfly und Solaris verwendet Reader getrandom(2), falls // verfügbar, ansonsten /dev/urandom. // Auf OpenBSD und macOS verwendet Reader getentropy(2). // Auf anderen Unix-ähnlichen Systemen liest Reader aus /dev/urandom. // Auf Windows-Systemen verwendet Reader die RtlGenRandom API. // Auf Wasm verwendet Reader die Web Crypto API. var Reader io.Reader // Read ist eine Hilfsfunktion, die Reader.Read unter Verwendung von io.ReadFull aufruft. // Bei der Rückgabe gilt n == len(b) genau dann, wenn err == nil. func Read(b []byte) (n int, err error) { return io.ReadFull(Reader, b) }
Zusammenfassung
- Von Programmen erzeugte Zufallszahlen sind pseudozufällig.
- In Go-Versionen vor 1.20 verwendete das Paket
math/rand
einen festen Seed (seed=1
) für die standardmäßige, gemeinsam genutzte Quelle, was zu identischen Zufallszahlensequenzen für jede Programmausführung führte. Ab Go 1.20 wird der Seed automatisch initialisiert, wodurch unterschiedliche Sequenzen sichergestellt werden, sofern er nicht explizit überschrieben wird. - Das Paket
crypto/rand
bietet einen kryptografisch sicheren Zufallszahlengenerator. - Obwohl
crypto/rand
sicherer ist alsmath/rand
, ist es mit Leistungseinbußen verbunden. Wählen Sie die passende Bibliothek basierend auf Ihrem Anwendungsfall. Für kryptografische Zwecke sollten Sie immer das Paketcrypto/rand
verwenden. - In JavaScript ist
crypto.getRandomValues
ein kryptografisch sicherer Zufallszahlengenerator, was ihn sicherer macht alsMath.random
.
Wir sind Leapcell, Ihre erste Wahl für die Bereitstellung von Go-Projekten in der Cloud.
Leapcell ist die Next-Gen Serverless Plattform für Webhosting, Async Tasks und Redis:
Multi-Language Support
- Entwickeln Sie mit Node.js, Python, Go oder Rust.
Deploy unlimited projects for free
- zahlen Sie nur für die Nutzung - keine Anfragen, keine Gebühren.
Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: $25 unterstützt 6,94 Millionen Anfragen bei einer durchschnittlichen Antwortzeit von 60 ms.
Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollautomatische CI/CD-Pipelines und GitOps-Integration.
- Echtzeit-Metriken und -Protokollierung für verwertbare Erkenntnisse.
Mühelose Skalierbarkeit und hohe Leistung
- Auto-Scaling zur einfachen Bewältigung hoher Parallelität.
- Kein operativer Overhead - konzentrieren Sie sich einfach auf das Bauen.
Erfahren Sie mehr in der Dokumentation!
Folgen Sie uns auf X: @LeapcellHQ