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'amorçage en utilisant time
apporte le même nombre aléatoire pendant un certain temps, de sorte que la boucle se répète encore et encore. Comment puis-je améliorer mon code?
Réponses:
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 la graine sur l'heure dans une boucle rapide, vous l'appellerez probablement plusieurs fois avec la même graine.
Dans votre cas, lorsque vous appelez votre
randInt
fonction jusqu'à ce que vous ayez une valeur différente, vous attendez que le temps (tel que renvoyé par Nano) change.Comme pour toutes les bibliothèques pseudo-aléatoires , vous ne devez définir la valeur de départ qu'une seule fois, par exemple lors de l'initialisation de votre programme, sauf si vous avez spécifiquement besoin de reproduire une séquence donnée (ce qui n'est généralement fait 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
rand.Seed(time.Now().UTC().UnixNano())
ligne de la fonction randInt au début de la main et tout sera plus rapide.Notez également que je pense que vous pouvez simplifier la construction de votre chaîne:
la source
rand.Seed(...)
à la fonctioninit()
.init()
est appelé automatiquement avantmain()
. Notez que vous n'avez pas besoin d'appelerinit()
depuismain()
!math/rand
n'est de toute façon pas sécurisé par cryptographie. Si c'est une exigence,crypto/rand
doit être utilisé.Je ne comprends pas pourquoi les gens sèment avec une valeur temps. Cela n'a jamais été une bonne idée d'après mon expérience. Par exemple, alors que l'horloge système est peut-être représentée en nanosecondes, la précision d'horloge du système n'est pas en nanosecondes.
Ce programme ne doit pas être exécuté sur le terrain de jeu Go, mais si vous l'exécutez sur votre machine, vous obtenez une estimation approximative du type de précision auquel vous pouvez vous attendre. Je vois des incréments d'environ 1000000 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.
Le degré auquel cela compte pour vous variera, mais vous pouvez éviter les pièges des valeurs de départ basées sur l'horloge en utilisant simplement le
crypto/rand.Read
comme source pour votre semence. Cela vous donnera cette qualité non déterministe que vous recherchez probablement dans vos nombres aléatoires (même si la mise en œuvre réelle elle-même est limitée à un ensemble de séquences aléatoires distinctes et déterministes).En guise de note secondaire, mais en relation avec votre question. Vous pouvez créer le vôtre en
rand.Source
utilisant cette méthode pour éviter le coût d'avoir des verrous protégeant la source. Lesrand
fonctions utilitaires du package 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 le vôtreSource
et l'utiliser 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 les itérations, il n'a jamais été conçu pour être utilisé de cette façon.la source
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 censée être saisie manuellement par un humain; l'exclusion de 0, O, 1 et l peut aider à réduire les erreurs de l'utilisateur.
et je place généralement la graine à l'intérieur d'un
init()
bloc. Ils sont documentés ici: http://golang.org/doc/effective_go.html#initla source
-1
enrand.Intn(len(alpha)-1)
. C'est parce querand.Intn(n)
renvoie toujours un nombre qui est inférieur àn
(en d'autres termes: de zéro àn-1
inclus).-1
inlen(alpha)-1
aurait garanti que le numéro 9 n'a jamais été utilisé dans la séquence.OK pourquoi si complexe!
Ceci est basé sur le code de Dystroy mais adapté à mes besoins.
C'est die six (rands ints
1 =< i =< 6
)La fonction ci-dessus est exactement la même chose.
J'espère que ces informations ont été utiles.
la source
3 5 2 5 4 2 5 6 3 1
rand.Intn()
, sinon vous obtiendrez toujours le même numéro à chaque fois que vous exécuterez votre programme.var bytes int
? Quelle est la différence de changer ce qui précèdebytes = rand.Intn(6)+1
enbytes := rand.Intn(6)+1
? Ils semblent tous les deux fonctionner pour moi, est-ce que l'un d'eux est sous-optimal pour une raison quelconque?Ce sont des nanosecondes, quelles sont les chances d'obtenir la même graine deux fois.
Quoi qu'il en soit, merci pour l'aide, voici ma solution finale basée sur toutes les entrées.
la source
what are the chances of getting the exact the exact same [nanosecond] twice?
Excellent. Tout dépend de la précision interne de l' implémentation des runtimes golang. Même si les unités sont en nanosecondes, le plus petit incrément peut être d'une milli-seconde ou même d'une seconde.Si votre objectif est simplement de générer une piqûre de nombre aléatoire, je pense qu'il n'est pas nécessaire de le compliquer avec plusieurs appels de fonction ou de réinitialiser la graine à chaque fois.
L'étape la plus importante consiste à appeler la fonction d'amorçage une seule fois avant de l'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 la fonction réelle au générateur de nombres pseudo-aléatoires.Voici un exemple de code créant une chaîne de nombres aléatoires
La raison pour laquelle j'ai utilisé Sprintf est qu'il permet un formatage de chaîne simple.
De plus, In
rand.Intn(7)
Intn renvoie, sous la forme d'un int, un nombre pseudo-aléatoire non négatif dans [0,7).la source
@ [Denys Séguret] a posté correctement. Mais dans mon cas, j'ai besoin d'une nouvelle graine à chaque fois, donc sous le code;
Incase vous avez besoin de fonctions rapides. J'utilise comme ça.
la source
la source
Petite mise à jour en raison du changement de l'API de Golang, veuillez omettre .UTC ():
c'est l'heure(). UTC () .UnixNano () -> heure.Maintenant (). UnixNano ()
la source