Maison > développement back-end > Golang > le corps du texte

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

Linda Hamilton
Libérer: 2024-10-24 01:40:02
original
419 Les gens l'ont consulté

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>
Copier après la connexion

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>
Copier après la connexion
Copier après la connexion

par :

<code class="go">var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>
Copier après la connexion
Copier après la connexion

Ou mieux encore :

<code class="go">const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"</code>
Copier après la connexion

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>
Copier après la connexion

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)
}
Copier après la connexion

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>
Copier après la connexion

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>
Copier après la connexion
Copier après la connexion

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>
Copier après la connexion
Copier après la connexion

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!

source:php
Déclaration de ce site Web
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
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!