190 votes

Comment ensemencer correctement un générateur de nombres aléatoires

J'essaie de générer une chaîne aléatoire en 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. Ensemencement à l'aide de time apporte le même numéro aléatoire pendant un certain temps, de sorte que la boucle itère encore et encore. Comment puis-je améliorer mon code ?

2 votes

Le "if string(randInt(65,90))!=temp {" ressemble à une tentative d'ajouter une sécurité supplémentaire, mais les choses se ressemblent les unes après les autres par hasard. En faisant cela, vous pouvez en fait diminuer l'entropie.

3 votes

Par ailleurs, il n'est pas nécessaire de convertir en UTC dans "time.Now().UTC().UnixNano()". Le temps Unix est calculé depuis Epoch qui est UTC de toute façon.

2 votes

Vous devez définir la graine une fois, une seule fois, et jamais plus d'une fois. Si votre application fonctionne pendant plusieurs jours, vous pouvez la définir une fois par jour.

0voto

RoboTamer Points 1508

Ce sont des nanosecondes, quelles sont les chances d'obtenir deux fois la même graine.
Quoi qu'il en soit, merci pour votre aide, voici ma solution finale basée sur tous les commentaires.

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))
}

1 votes

Re : what are the chances of getting the exact the exact same [nanosecond] twice? Excellent. Tout dépend de la précision interne de la mise en œuvre des runtimes golang. Bien que les unités soient les nano-secondes, le plus petit incrément peut être une milli-seconde ou même une seconde.

0voto

Captain Levi Points 614

Si votre objectif est simplement de générer un nombre aléatoire, je pense qu'il n'est pas nécessaire de le compliquer avec de multiples appels de fonctions ou de réinitialiser la graine à chaque fois.

L'étape la plus importante est d'appeler la fonction d'amorçage une seule fois avant de l'exécuter. rand.Init(x) . Semences utilise la valeur d'amorçage fournie pour initialiser la source par défaut à un état déterministe. Il est donc conseillé de l'appeler une fois avant l'appel de la fonction réelle du 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)
}

La raison pour laquelle j'ai utilisé Sprintf est parce qu'il permet un formatage simple des chaînes de caractères.

De plus, en rand.Intn(7) Intn renvoie, sous la forme d'un int, un nombre pseudo-aléatoire non-négatif dans [0,7).

0voto

STEEL Points 649

@ [Denys Séguret] a posté correctement. Mais dans mon cas, j'ai besoin d'une nouvelle graine à chaque fois, d'où le code ci-dessous ;

Au cas où vous auriez besoin de fonctions rapides. Je les utilise comme ça.

func RandInt(min, max int) int {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    return r.Intn(max-min) + min
}

func RandFloat(min, max float64) float64 {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    return min + r.Float64()*(max-min)
}

source

0voto

0example.com Points 209

Chaque fois que la méthode randint() est appelée à l'intérieur de la boucle for, une graine différente est définie et une séquence est générée en fonction de la méthode temps . Mais comme la boucle for fonctionne rapidement dans votre ordinateur, en peu de temps, la graine est presque la même et une séquence très similaire à la précédente est générée en raison de l'effet de la boucle for. temps . Il suffit donc de définir la graine en dehors de la méthode randint().

package main

import (
    "bytes"
    "fmt"
    "math/rand"
    "time"
)

var r = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
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 {
    return min + r.Intn(max-min)
}

-2voto

letanthang Points 187

Petite mise à jour due à un changement d'api golang, veuillez omettre .UTC() :

time.Now(). UTC() .UnixNano() -> time.Now().UnixNano()

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())
    fmt.Println(randomInt(100, 1000))
}

func randInt(min int, max int) int {
    return min + rand.Intn(max-min)
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X