Maison  >  Article  >  développement back-end  >  Comment générer une chaîne rapide et aléatoire d’une longueur définie en Go ?

Comment générer une chaîne rapide et aléatoire d’une longueur définie en Go ?

Linda Hamilton
Linda Hamiltonoriginal
2024-10-24 01:40:02420parcourir

How Do I Generate a Quick, Random String of a Set Length in Go?

Comment générer une chaîne aléatoire d'une longueur fixe dans Go ?

Problème

Je veux une chaîne aléatoire de caractères uniquement (majuscules ou minuscules ), pas de chiffres, dans Go. Quelle est la manière la plus rapide et la plus simple de procéder ?

Réponse

La question recherche l'approche "la plus rapide et la plus simple". La réponse de Paul propose une technique simple. Cependant, considérons également l'aspect « le plus rapide ». Nous affinerons notre code de manière itérative pour arriver à une solution optimisée.

I. Améliorations

1. Genesis (Runes)

La solution initiale que nous allons optimiser est :

<code class="go">import (
    "math/rand"
    "time"
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func RandStringRunes(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letterRunes[rand.Intn(len(letterRunes))]
    }
    return string(b)
}</code>

2. Octets

Si les caractères utilisés pour la chaîne aléatoire sont limités aux alphabets anglais majuscules et minuscules, nous pouvons travailler avec des octets car les lettres de l'alphabet anglais mappent 1 à 1 sur les octets dans l'encodage UTF-8 ( que Go utilise pour stocker les chaînes).

Nous pouvons donc remplacer :

<code class="go">var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>

par :

<code class="go">var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>

Ou mieux encore :

<code class="go">const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"</code>

C'est une amélioration significative car nous pouvons désormais utiliser une const (Go prend en charge les constantes de chaîne mais pas les constantes de tranche). De plus, l'expression len(letters) sera également constante.

3. Reste

Les solutions précédentes déterminaient un nombre aléatoire pour une lettre en appelant rand.Intn() (qui délègue à Rand.Intn() et ensuite à Rand.Int31n()).

C'est plus lent que d'utiliser rand.Int63() qui produit un nombre aléatoire avec 63 bits aléatoires.

Nous pouvons donc simplement appeler rand.Int63() et utiliser le reste après avoir divisé par len(lettres) :

<code class="go">func RandStringBytesRmndr(n int) string {
    b := make([]byte, n)
    for i := range b {
        b[i] = letters[rand.Int63() % int64(len(letters))]
    }
    return string(b)
}</code>

C'est plus rapide tout en maintenant une distribution de probabilité égale de toutes les lettres (bien que la distorsion soit négligeable, le nombre de lettres 52 est bien inférieur à 1<<63 - 1).

4. Masquage

Nous pouvons maintenir une répartition égale des lettres en utilisant uniquement les bits les plus bas du nombre aléatoire, suffisamment pour représenter le nombre de lettres. Pour 52 lettres, 6 bits sont nécessaires : 52 = 110100b. Nous n'utiliserons donc que les 6 bits les plus bas du nombre renvoyé par rand.Int63().

Nous "acceptons" également le nombre uniquement s'il se situe dans la plage 0..len(letterBytes)-1 . Si les bits les plus bas sont plus gros, nous les rejetons et demandons un nouveau numéro.

<code class="go">const (
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
)

func RandStringBytesMask(n int) string {
    b := make([]byte, n)
    for i := 0; i < n; {
        if idx := int(rand.Int63() & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i++
        }
    }
    return string(b)
}

5. Masquage amélioré

La solution précédente utilise uniquement les 6 bits les plus bas des 63 bits aléatoires de rand.Int63(). Ceci est inefficace car l'obtention des bits aléatoires est la partie la plus lente de notre algorithme.

Comme nous avons 52 lettres, 6 bits codent un index de lettres. Les 63 bits aléatoires peuvent désigner 63/6 = 10 indices de lettres différentes. Utilisons les 10 :

const (
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1<= 0; {
        if remain == 0 {
            cache, remain = rand.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i--
        }
        cache >>= letterIdxBits
        remain--
    }
    return string(b)
}

6. Source

Masquage amélioré est assez efficace. Considérons un autre aspect : la source des nombres aléatoires.

Le package crypto/rand fournit la fonction Read(b []byte). Cependant, cela n'améliorerait pas les performances car crypto/rand implémente un générateur de nombres pseudo-aléatoires cryptographiquement sécurisé, qui est plus lent.

Nous nous en tiendrons donc au package math/rand. rand.Rand utilise un rand.Source comme source de bits aléatoires. On peut donc utiliser un rand.Source directement :

<code class="go">import (
    "math/rand"
    "time"
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func RandStringRunes(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letterRunes[rand.Intn(len(letterRunes))]
    }
    return string(b)
}</code>

7. Utilisation de strings.Builder

Les solutions précédentes renvoyaient des chaînes construites d'abord dans une tranche ([]rune dans Genesis et []byte par la suite), puis converties en chaîne. Cette conversion finale nécessite de copier le contenu de la tranche car les valeurs de chaîne sont immuables.

Go 1.10 a introduit strings.Builder. Ce nouveau type peut être utilisé pour créer du contenu de chaîne de la même manière que bytes.Buffer. Il utilise un []octet en interne et n'a pas besoin de copier le contenu pour produire la chaîne.

<code class="go">var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>

8. "Imiter" strings.Builder avec package unsafe

strings.Builder construit une chaîne dans un []octet interne, tout comme nous l'avons fait nous-mêmes. Ainsi, l'utilisation de strings.Builder introduit une surcharge, que nous avons uniquement modifiée pour éviter la copie finale.

Cependant, nous pouvons également éviter cette copie en utilisant package unsafe :

<code class="go">var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn