Ich versuche, eine zufällige Zeichenfolge in Go zu generieren, und hier ist der Code, den ich bisher geschrieben habe:
package main
import (
"bytes"
"fmt"
"math/rand"
"time"
)
func main() {
fmt.Println(randomString(10))
}
func randomString(l int) string {
var result bytes.Buffer
var temp string
for i := 0; i < l; {
if string(randInt(65, 90)) != temp {
temp = string(randInt(65, 90))
result.WriteString(temp)
i++
}
}
return result.String()
}
func randInt(min int, max int) int {
rand.Seed(time.Now().UTC().UnixNano())
return min + rand.Intn(max-min)
}
Meine Implementierung ist sehr langsam. Seeding using time
bringt für eine bestimmte Zeit dieselbe Zufallszahl, sodass die Schleife immer wieder wiederholt wird. Wie kann ich meinen Code verbessern?
Antworten:
Jedes Mal, wenn Sie denselben Startwert festlegen, erhalten Sie dieselbe Sequenz. Wenn Sie also den Startwert in einer schnellen Schleife auf die Zeit einstellen, werden Sie ihn wahrscheinlich viele Male mit demselben Startwert aufrufen.
In Ihrem Fall
randInt
warten Sie beim Aufrufen Ihrer Funktion, bis Sie einen anderen Wert haben, darauf, dass sich die Zeit (wie von Nano zurückgegeben) ändert.Wie bei allen Pseudozufallsbibliotheken müssen Sie den Startwert nur einmal festlegen, z. B. beim Initialisieren Ihres Programms, es sei denn, Sie müssen eine bestimmte Sequenz speziell reproduzieren (dies wird normalerweise nur zum Debuggen und Testen von Einheiten durchgeführt).
Danach rufen Sie einfach
Intn
auf, um die nächste zufällige Ganzzahl zu erhalten.Bewegen Sie die
rand.Seed(time.Now().UTC().UnixNano())
Zeile von der Funktion randInt zum Anfang der Hauptfunktion, und alles wird schneller.Beachten Sie auch, dass Sie meiner Meinung nach das Erstellen von Zeichenfolgen vereinfachen können:
quelle
rand.Seed(...)
die Funktion auch erweiterninit()
.init()
wird vorher automatisch aufgerufenmain()
. Beachten Sie, dass Sie nicht brauchen , um Anrufinit()
ausmain()
!math/rand
ist ohnehin nicht kryptografisch sicher. Wenn dies erforderlich ist,crypto/rand
sollte verwendet werden.Ich verstehe nicht, warum Leute mit einem Zeitwert säen. Dies war meiner Erfahrung nach nie eine gute Idee. Während beispielsweise die Systemuhr möglicherweise in Nanosekunden dargestellt wird, beträgt die Taktgenauigkeit des Systems keine Nanosekunden.
Dieses Programm sollte nicht auf dem Go-Spielplatz ausgeführt werden. Wenn Sie es jedoch auf Ihrem Computer ausführen, erhalten Sie eine grobe Schätzung der zu erwartenden Genauigkeit. Ich sehe Inkremente von ungefähr 1000000 ns, also Inkremente von 1 ms. Das sind 20 Bit Entropie, die nicht verwendet werden. Währenddessen sind die hohen Bits meist konstant.
Der Grad, in dem dies für Sie von Bedeutung ist, variiert, aber Sie können Fallstricke von uhrbasierten Startwerten vermeiden, indem Sie einfach die
crypto/rand.Read
als Quelle für Ihren Startwert verwenden . Sie erhalten die nicht deterministische Qualität, nach der Sie wahrscheinlich in Ihren Zufallszahlen suchen (selbst wenn die tatsächliche Implementierung selbst auf eine Reihe unterschiedlicher und deterministischer Zufallssequenzen beschränkt ist).Als Randnotiz aber in Bezug auf Ihre Frage.
rand.Source
Mit dieser Methode können Sie eigene erstellen , um die Kosten für Sperren zum Schutz der Quelle zu vermeiden. Dierand
Funktionen des Paketdienstprogramms sind praktisch, verwenden jedoch auch Schlösser unter der Haube, um zu verhindern, dass die Quelle gleichzeitig verwendet wird. Wenn Sie das nicht benötigen, können Sie es vermeiden, indem Sie Ihre eigenen erstellenSource
und diese nicht gleichzeitig verwenden. Unabhängig davon sollten Sie Ihren Zufallszahlengenerator zwischen den Iterationen NICHT neu säen, er wurde nie für diese Verwendung entwickelt.quelle
Nur um es für die Nachwelt wegzuwerfen: Manchmal kann es vorzuziehen sein, eine zufällige Zeichenfolge mit einer anfänglichen Zeichensatzzeichenfolge zu generieren. Dies ist nützlich, wenn die Zeichenfolge von einem Menschen manuell eingegeben werden soll. Das Ausschließen von 0, O, 1 und l kann dazu beitragen, Benutzerfehler zu reduzieren.
und ich setze normalerweise den Samen innerhalb eines
init()
Blocks. Sie sind hier dokumentiert: http://golang.org/doc/effective_go.html#initquelle
-1
inrand.Intn(len(alpha)-1)
. Dies liegt daran, dassrand.Intn(n)
immer eine Zahl zurückgegeben wird, die kleiner als istn
(mit anderen Worten: von Null bisn-1
einschließlich).-1
Inlen(alpha)-1
garantiert, dass die Nummer 9 nie in der Sequenz verwendet wurde.OK warum so komplex!
Dies basiert auf dem Code des Dystroy, ist aber für meine Bedürfnisse geeignet.
Es ist die sechs (Rand Ints
1 =< i =< 6
)Die obige Funktion ist genau das gleiche.
Ich hoffe, diese Informationen waren von Nutzen.
quelle
3 5 2 5 4 2 5 6 3 1
rand.Intn()
, andernfalls erhalten Sie immer die gleiche Nummer, wenn Sie Ihr Programm ausführen.var bytes int
? Was ist der Unterschied der oben auf sich änderndebytes = rand.Intn(6)+1
zubytes := rand.Intn(6)+1
? Sie scheinen beide für mich zu arbeiten. Ist einer von ihnen aus irgendeinem Grund nicht optimal?Es sind Nanosekunden, wie hoch sind die Chancen, zweimal denselben Samen zu bekommen?
Wie auch immer, danke für die Hilfe, hier ist meine Endlösung, die auf allen Eingaben basiert.
quelle
what are the chances of getting the exact the exact same [nanosecond] twice?
Ausgezeichnet. Es hängt alles von der internen Genauigkeit der Implementierung der Golang-Laufzeiten ab. Obwohl die Einheiten Nanosekunden sind, kann das kleinste Inkrement eine Millisekunde oder sogar eine Sekunde sein.Wenn Sie nur einen Stich einer Zufallszahl erzeugen möchten, ist es meines Erachtens nicht erforderlich, ihn jedes Mal durch mehrere Funktionsaufrufe oder das Zurücksetzen des Startwerts zu komplizieren.
Der wichtigste Schritt besteht darin, die Seed-Funktion nur einmal aufzurufen, bevor sie tatsächlich ausgeführt wird
rand.Init(x)
. Seed verwendet den angegebenen Seed-Wert, um die Standardquelle in einen deterministischen Zustand zu initialisieren. Es wird daher empfohlen, es einmal vor dem eigentlichen Funktionsaufruf des Pseudozufallszahlengenerators aufzurufen.Hier ist ein Beispielcode, der eine Folge von Zufallszahlen erstellt
Der Grund, warum ich Sprintf verwendet habe, ist, dass es eine einfache Formatierung von Zeichenfolgen ermöglicht.
Außerdem gibt In
rand.Intn(7)
Intn als int eine nicht negative Pseudozufallszahl in [0,7] zurück.quelle
@ [Denys Séguret] hat richtig gepostet. Aber in meinem Fall brauche ich jedes Mal neuen Samen, daher unter dem Code;
Für den Fall, dass Sie schnelle Funktionen benötigen. Ich benutze so.
Quelle
quelle
Kleines Update aufgrund einer Änderung der Golang-API, bitte .UTC () weglassen:
time.Now (). UTC () .UnixNano () -> time.Now (). UnixNano ()
quelle