"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > ¿Cómo genero una cadena rápida y aleatoria de una longitud determinada en Go?

¿Cómo genero una cadena rápida y aleatoria de una longitud determinada en Go?

Publicado el 2024-11-14
Navegar:524

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

¿Cómo generar una cadena aleatoria de una longitud fija en Go?

Problema

Quiero una cadena aleatoria de caracteres únicamente (mayúsculas o minúsculas) ), sin números, en Go. ¿Cuál es la forma más rápida y sencilla de hacer esto?

Respuesta

La pregunta busca el enfoque "más rápido y sencillo". La respuesta de Paul ofrece una técnica sencilla. Sin embargo, consideremos también el aspecto "más rápido". Refinaremos nuestro código de forma iterativa y llegaremos a una solución optimizada.

I. Mejoras

1. Génesis (Runas)

La solución inicial que optimizaremos es:

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

2. Bytes

Si los caracteres utilizados para la cadena aleatoria están restringidos a alfabetos ingleses en mayúsculas y minúsculas, podemos trabajar con bytes porque las letras del alfabeto inglés se asignan 1 a 1 a bytes en codificación UTF-8 ( que Go usa para almacenar cadenas).

Así que podemos reemplazar:

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

con:

var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

o mejor aún:

const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
esto es una mejora significativa porque ahora podemos usar una constante (Go admite constantes de cadena pero no constantes de corte). Además, la expresión len(letras) también será constante.

3. Resto

Las soluciones anteriores determinaron un número aleatorio para una letra llamando a rand.Intn() (que delega a Rand.Intn() y luego a Rand.Int31n()).

Esto es más lento que usar rand.Int63() que produce un número aleatorio con 63 bits aleatorios.

Así que podemos simplemente llamar a rand.Int63() y usar el resto después de dividir por len(letras):

func RandStringBytesRmndr(n int) string {
    b := make([]byte, n)
    for i := range b {
        b[i] = letters[rand.Int63() % int64(len(letters))]
    }
    return string(b)
}
Esto es más rápido y mantiene una distribución de probabilidad igual de todas las letras (aunque la distorsión es insignificante, el número de letras 52 es mucho menor que 1

4. Enmascaramiento

Podemos mantener una distribución equitativa de letras usando solo los bits más bajos del número aleatorio, suficientes para representar el número de letras. Para 52 letras, se requieren 6 bits: 52 = 110100b. Por lo tanto, solo usaremos los 6 bits más bajos del número devuelto por rand.Int63().

También solo "aceptamos" el número si se encuentra dentro del rango 0..len(letterBytes)-1 . Si los bits más bajos son mayores, los descartamos y solicitamos un nuevo número.

const (
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1

5. Enmascaramiento mejorado

La solución anterior solo utiliza los 6 bits más bajos de los 63 bits aleatorios de rand.Int63(). Esto es ineficiente ya que obtener los bits aleatorios es la parte más lenta de nuestro algoritmo.

Dado que tenemos 52 letras, 6 bits codifican un índice de letras. Los 63 bits aleatorios pueden designar 63/6 = 10 índices de letras diferentes. Usemos los 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 >= letterIdxBits
        remain--
    }
    return string(b)
}

6. Fuente

Enmascaramiento mejorado es bastante eficiente. Consideremos otro aspecto: la fuente de números aleatorios.

El paquete crypto/rand proporciona la función Leer(b []byte). Sin embargo, esto no mejoraría el rendimiento porque crypto/rand implementa un generador de números pseudoaleatorios criptográficamente seguro, que es más lento.

Así que nos ceñiremos al paquete math/rand. rand.Rand utiliza rand.Source como fuente de bits aleatorios. Entonces podemos usar rand.Source directamente:

var src = rand.NewSource(time.Now().UnixNano())

func RandStringBytesMaskImprSrc(n int) string {
    b := make([]byte, n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = src.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx >= letterIdxBits
        remain--
    }
    return string(b)
}

7. Utilizando strings.Builder

Las soluciones anteriores devolvieron cadenas construidas primero en un segmento ([]runa en

Genesis y []byte posteriormente) y luego convertidas en cadena. Esta conversión final requiere copiar el contenido del segmento porque los valores de cadena son inmutables.

Go 1.10 introdujo strings.Builder. Este nuevo tipo se puede utilizar para crear contenidos de cadena de forma similar a bytes.Buffer. Utiliza un []byte internamente y no necesita copiar el contenido para producir la cadena.

func RandStringBytesMaskImprSrcSB(n int) string {
    sb := strings.Builder{}
    sb.Grow(n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = src.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx >= letterIdxBits
        remain--
    }
    return sb.String()
}

8. "Imitando" cadenas.Builder con paquete unsafe

strings.Builder construye una cadena en un []byte interno, tal como lo hicimos nosotros. Entonces, usar strings.Builder introduce algunos gastos generales, que solo cambiamos para evitar la copia final.

Sin embargo, también podemos evitar esta copia usando el paquete unsafe:

func RandStringBytesMaskImprSrcUnsafe( norte int) cadena {
    b := crear([]byte, n)
    // Un src.Int63() genera 63 bits aleatorios, ¡suficientes para caracteres letterIdxMax!
    para i, caché, permanecer := n-1, src.Int63(), letterIdxMax; yo >= 0; {
        si permanece == 0 {
            caché, permanecer = src.Int63(), letterIdxMax
        }
        si idx := int(caché & letterIdxMask); idx 
Declaración de liberación Este artículo se reproduce en: 1729661378 Si hay alguna infracción, comuníquese con [email protected] para eliminarlo.
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3