J'essaie de générer une chaîne aléatoire dans Go et voici le code que j'ai écrit jusqu'à présent:
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)
}
Ma mise en œuvre est très lente. L'ensemencement à l'aide de time
apporte le même nombre aléatoire pendant un certain temps. La boucle est ainsi répétée. Comment puis-je améliorer mon code?
Chaque fois que vous définissez la même graine, vous obtenez la même séquence. Donc, bien sûr, si vous définissez l'heure au bon moment dans une boucle rapide, vous l'appellerez probablement avec le même signe plusieurs fois.
Dans votre cas, lorsque vous appelez votre fonction randInt
jusqu'à ce que vous ayez une valeur différente, vous attendez que le temps (renvoyé par Nano) change.
Comme pour toutes les bibliothèques pseudo-aléatoires , vous devez définir la graine une seule fois, par exemple lors de l'initialisation de votre programme, sauf si vous devez spécifiquement reproduire une séquence donnée (ce qui n'est généralement effectué que pour le débogage et les tests unitaires).
Après cela, vous appelez simplement Intn
pour obtenir le prochain entier aléatoire.
Déplacez la ligne Rand.Seed(time.Now().UTC().UnixNano())
de la fonction randInt au début de la fenêtre principale et tout ira plus vite.
Notez également que je pense que vous pouvez simplifier la construction de votre chaîne:
package main
import (
"fmt"
"math/Rand"
"time"
)
func main() {
Rand.Seed(time.Now().UTC().UnixNano())
fmt.Println(randomString(10))
}
func randomString(l int) string {
bytes := make([]byte, l)
for i := 0; i < l; i++ {
bytes[i] = byte(randInt(65, 90))
}
return string(bytes)
}
func randInt(min int, max int) int {
return min + Rand.Intn(max-min)
}
juste pour le jeter pour la postérité: il peut parfois être préférable de générer une chaîne aléatoire en utilisant une chaîne de jeu de caractères initiale. Ceci est utile si la chaîne est supposée être entrée manuellement par un humain; exclure 0, O, 1 et l peut aider à réduire les erreurs de l’utilisateur.
var alpha = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
// generates a random string of fixed size
func srand(size int) string {
buf := make([]byte, size)
for i := 0; i < size; i++ {
buf[i] = alpha[Rand.Intn(len(alpha))]
}
return string(buf)
}
et je place généralement la graine à l'intérieur d'un bloc init()
. Ils sont documentés ici: http://golang.org/doc/effective_go.html#init
OK pourquoi si complexe!
package main
import (
"fmt"
"math/Rand"
"time"
)
func main() {
Rand.Seed( time.Now().UnixNano())
var bytes int
for i:= 0 ; i < 10 ; i++{
bytes = Rand.Intn(6)+1
fmt.Println(bytes)
}
//fmt.Println(time.Now().UnixNano())
}
Ceci est basé sur le code de dystroy mais adapté à mes besoins.
C'est mourir six (rands ints 1 =< i =< 6
)
func randomInt (min int , max int ) int {
var bytes int
bytes = min + Rand.Intn(max)
return int(bytes)
}
La fonction ci-dessus est exactement la même chose.
J'espère que cette information a été utile.
Je ne comprends pas pourquoi les gens ensemencent avec une valeur temporelle. Dans mon expérience, cela n’a jamais été une bonne idée. Par exemple, alors que l'horloge système est peut-être représentée en nanosecondes, la précision de l'horloge du système n'est pas en nanosecondes.
Ce programme ne doit pas être exécuté sur le terrain de jeu de Go, mais si vous l’utilisez sur votre machine, vous obtenez une estimation approximative du type de précision que vous pouvez attendre. Je vois des incréments d’environ 1 000 ns, donc des incréments de 1 ms. C'est 20 bits d'entropie qui ne sont pas utilisés. Pendant tout ce temps, les bits hauts sont pour la plupart constants.
Cela dépendra de votre degré de pertinence, mais vous pouvez éviter les pièges des valeurs de départ basées sur une horloge en utilisant simplement le crypto/Rand.Read
comme source de votre départ. Cela vous donnera la qualité non déterministe que vous recherchez probablement dans vos nombres aléatoires (même si l'implémentation elle-même est limitée à un ensemble de séquences aléatoires distinctes et déterministes).
import (
crypto_Rand "crypto/Rand"
"encoding/binary"
math_Rand "math/Rand"
)
func init() {
var b [8]byte
_, err := crypto_Rand.Read(b[:])
if err != nil {
panic("cannot seed math/Rand package with cryptographically secure random number generator")
}
math_Rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
}
En note de côté mais par rapport à votre question. Vous pouvez créer votre propre Rand.Source
à l’aide de cette méthode pour éviter le coût des verrous protégeant la source. Les fonctions de l'utilitaire de package Rand
sont pratiques, mais elles utilisent également des verrous sous le capot pour empêcher l'utilisation simultanée de la source. Si vous n'en avez pas besoin, vous pouvez l'éviter en créant votre propre variable Source
et en l'utilisant de manière non simultanée. Quoi qu'il en soit, vous NE devriez PAS réensemencer votre générateur de nombres aléatoires entre deux itérations, il n'a jamais été conçu pour être utilisé de cette façon.
C'est nano secondes, quelles sont les chances d'obtenir deux fois la même graine.
Quoi qu'il en soit, merci pour l'aide, voici ma solution finale basée sur toutes les entrées.
package main
import (
"math/Rand"
"time"
)
func init() {
Rand.Seed(time.Now().UTC().UnixNano())
}
// generates a random string
func srand(min, max int, readable bool) string {
var length int
var char string
if min < max {
length = min + Rand.Intn(max-min)
} else {
length = min
}
if readable == false {
char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
} else {
char = "ABCDEFHJLMNQRTUVWXYZabcefghijkmnopqrtuvwxyz23479"
}
buf := make([]byte, length)
for i := 0; i < length; i++ {
buf[i] = char[Rand.Intn(len(char)-1)]
}
return string(buf)
}
// For testing only
func main() {
println(srand(5, 5, true))
println(srand(5, 5, true))
println(srand(5, 5, true))
println(srand(5, 5, false))
println(srand(5, 7, true))
println(srand(5, 10, false))
println(srand(5, 50, true))
println(srand(5, 10, false))
println(srand(5, 50, true))
println(srand(5, 10, false))
println(srand(5, 50, true))
println(srand(5, 10, false))
println(srand(5, 50, true))
println(srand(5, 4, true))
println(srand(5, 400, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
println(srand(6, 5, true))
}
Si votre objectif est simplement de générer une piqûre de nombre aléatoire, je pense qu'il est inutile de la compliquer avec plusieurs appels de fonction ou de réinitialiser la valeur initiale à chaque fois.
L'étape la plus importante consiste à appeler la fonction de départ une seule fois avant d'exécuter Rand.Init(x)
. Seed utilise la valeur de départ fournie pour initialiser la source par défaut à un état déterministe. Donc, il serait suggéré de l'appeler une fois avant l'appel de fonction réel au générateur de nombres pseudo-aléatoires.
Voici un exemple de code créant une chaîne de nombres aléatoires
package main
import (
"fmt"
"math/Rand"
"time"
)
func main(){
Rand.Seed(time.Now().UnixNano())
var s string
for i:=0;i<10;i++{
s+=fmt.Sprintf("%d ",Rand.Intn(7))
}
fmt.Printf(s)
}
J'ai utilisé Sprintf parce que cela permet un formatage simple.
De plus, In Rand.Intn(7)
Intn renvoie, en tant qu'int, un nombre pseudo-aléatoire non négatif dans [0,7).