web-dev-qa-db-fra.com

Générateur de nombres aléatoires Golang comment semer correctement

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?

126
copperMan

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)
}
191
Denys Séguret

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

15
jorelli

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.

12
Luviz

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.

2
John Leidegren

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

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

0
Yadhukrishna S Pai