number golang string random go

number - golang int to string



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

Además, he encontrado un paquete que tiene varios métodos para manipular datos falsos. Lo encontró útil para sembrar bases de datos mientras desarrolla https://github.com/Pallinder/go-randomdata . Podría ser útil para otra persona también

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


Aquí está mi manera) Use rand matemático o crypto rand como desee.

func randStr(len int) string { buff := make([]byte, len) rand.Read(buff) str := base64.StdEncoding.EncodeToString(buff) // Base 64 can be longer than len return str[:len] }


Dos opciones posibles (puede haber más, por supuesto):

  1. Puede usar el paquete crypto/rand que admite la lectura de matrices de bytes aleatorios (desde / dev / urandom) y está orientado a la generación aleatoria criptográfica. vea http://golang.org/pkg/crypto/rand/#example_Read . Sin embargo, podría ser más lenta que la generación normal de números pseudoaleatorios.

  2. Toma un número aleatorio y hash usando md5 o algo así.


La solución de Paul proporciona una solución simple y general.

La pregunta pregunta por "la forma más rápida y sencilla" . Vamos a abordar esto. Llegaremos a nuestro código final, más rápido, de manera iterativa. Al final de la respuesta se puede encontrar una evaluación comparativa de cada iteración.

Todas las soluciones y el código de referencia se pueden encontrar en Go Playground . El código en el Playground es un archivo de prueba, no un archivo ejecutable. XX_test.go guardarlo en un archivo llamado XX_test.go y ejecutarlo con go test -bench . .

I. mejoras

1. Génesis (Runas)

Como recordatorio, la solución original y general que estamos mejorando es la siguiente:

func init() { rand.Seed(time.Now().UnixNano()) } 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 para elegir y ensamblar la cadena aleatoria contienen solo las letras mayúsculas y minúsculas del alfabeto inglés, podemos trabajar con bytes solo porque las letras del alfabeto inglés se asignan a los bytes 1 a 1 en la codificación UTF-8 (que Así es como Go almacena cuerdas).

Así que en lugar de:

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

nosotros podemos usar:

var letters = []bytes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

O mejor:

const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

Ahora esto ya es una gran mejora: podríamos lograrlo para ser una const (hay constantes de string pero no hay constantes de división ). Como ganancia adicional, la expresión len(letters) también será una const ! (La expresión len(s) es constante si s es una constante de cadena.)

¿Y a qué costo? Nada en absoluto. string s puede ser indexada, lo que indexa sus bytes, perfecto, exactamente lo que queremos.

Nuestro próximo destino se ve así:

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" func RandStringBytes(n int) string { b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Intn(len(letterBytes))] } return string(b) }

3. el resto

Las soluciones anteriores obtienen un número aleatorio para designar una letra aleatoria llamando a rand.Intn() que delega a Rand.Intn() que delega a Rand.Int31n() .

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

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

func RandStringBytesRmndr(n int) string { b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))] } return string(b) }

Esto funciona y es significativamente más rápido, la desventaja es que la probabilidad de que todas las letras no sean exactamente iguales (asumiendo que rand.Int63() produce todos los números de 63 bits con igual probabilidad). Aunque la distorsión es extremadamente pequeña, ya que el número de letras 52 es mucho menor que 1<<63 - 1 , en la práctica esto es perfectamente correcto.

Para que esto sea más fácil de entender: digamos que desea un número aleatorio en el rango de 0..5 . Usando 3 bits aleatorios, esto produciría los números 0..1 con doble probabilidad que desde el rango 2..5 . Usando 5 bits aleatorios, se producirían números en el rango 0..1 con probabilidad 6/32 y números en el rango 2..5 con probabilidad 5/32 que ahora está más cerca del deseado. Aumentar el número de bits hace que esto sea menos significativo, al alcanzar los 63 bits, es insignificante.

4. Enmascaramiento

Sobre la base de la solución anterior, podemos mantener la distribución equitativa de las letras utilizando solo la cantidad de bits más bajos del número aleatorio, ya que se requieren tantas para representar la cantidad de letras. Entonces, por ejemplo, si tenemos 52 letras, se requieren 6 bits para representarlo: 52 = 110100b . Por lo tanto, solo usaremos los 6 bits más bajos del número devuelto por rand.Int63() . Y para mantener la distribución equitativa de las letras, solo "aceptamos" el número si está dentro del rango 0..len(letterBytes)-1 . Si los bits más bajos son mayores, lo descartamos y consultamos un nuevo número aleatorio.

Tenga en cuenta que la probabilidad de que los bits más bajos sean mayores o iguales a len(letterBytes) es menor que 0.5 en general ( 0.25 en promedio), lo que significa que incluso si este fuera el caso, repetir este caso "raro" disminuye el posibilidad de no encontrar un buen número. Después de n repeticiones, la probabilidad de que aún no tengamos un buen índice es mucho menor que pow(0.5, n) , y esto es solo una estimación superior. En el caso de 52 letras, la probabilidad de que los 6 bits más bajos no sean buenos es solo (64-52)/64 = 0.19 ; lo que significa, por ejemplo, que las posibilidades de no tener un buen número después de 10 repeticiones son 1e-8 .

Así que aquí está la solución:

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 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. Enmascaramiento mejorado

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

Si tenemos 52 letras, eso significa que 6 bits codifican un índice de letras. Así que 63 bits aleatorios pueden designar 63/6 = 10 índices de letras diferentes. Usemos todos esos 10:

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" const ( letterIdxBits = 6 // 6 bits to represent a letter index letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits ) func RandStringBytesMaskImpr(n int) string { b := make([]byte, n) // A rand.Int63() generates 63 random bits, enough for letterIdxMax letters! for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 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. Fuente

El Enmascaramiento Mejorado es bastante bueno, no podemos mejorarlo mucho. Podríamos, pero no vale la pena la complejidad.

Ahora encontremos algo más para mejorar. La fuente de los números aleatorios.

Hay un paquete crypto/rand que proporciona una función de Read(b []byte) , por lo que podríamos usarlo para obtener tantos bytes con una sola llamada como tantos necesitamos. Esto no ayudaría en términos de rendimiento, ya que crypto/rand implementa un generador de números pseudoaleatorios criptográficamente seguro, por lo que es mucho más lento.

Así que sigamos con el paquete math/rand . El rand.Rand utiliza un rand.Source como fuente de bits aleatorios. rand.Source es una interfaz que especifica un Int63() int64 : exactamente y lo único que necesitábamos y utilizamos en nuestra última solución.

Así que realmente no necesitamos un rand.Rand (ya sea explícito o global, compartimos el paquete del rand ), un rand.Source es lo suficientemente perfecto para nosotros

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 < len(letterBytes) { b[i] = letterBytes[idx] i-- } cache >>= letterIdxBits remain-- } return string(b) }

También tenga en cuenta que esta última solución no requiere que inicialice (inicialice) el Rand global del paquete math/rand , ya que no se utiliza (y nuestra rand.Source se inicializa / siembra correctamente).

Una cosa más a tener en cuenta aquí: el paquete doc de math/rand afirma:

La Fuente predeterminada es segura para el uso simultáneo de múltiples goroutines.

Por lo tanto, la fuente predeterminada es más lenta que una Source que puede ser obtenida por rand.NewSource() , porque la fuente predeterminada debe proporcionar seguridad bajo el acceso / uso concurrente, mientras que rand.NewSource() no ofrece esto (y por lo tanto la Source devuelta). Por eso es más probable que sea más rápido).

(7. Usando rand.Read() )

Go 1.7 agregó una función rand.Read() y un método Rand.Read() . Deberíamos tener la tentación de utilizarlos para leer tantos bytes como sea necesario en un solo paso, a fin de lograr un mejor rendimiento.

Hay un pequeño "problema" con esto: ¿cuántos bytes necesitamos? Podríamos decir: tantos como el número de letras de salida. Pensaríamos que esta es una estimación superior, ya que un índice de letras usa menos de 8 bits (1 byte). Pero en este punto ya lo estamos haciendo peor (ya que obtener los bits aleatorios es la "parte difícil"), y estamos obteniendo más de lo necesario.

También tenga en cuenta que para mantener una distribución equitativa de todos los índices de letras, podría haber algunos datos aleatorios de "basura" que no podremos usar, por lo que terminaríamos omitiendo algunos datos y, por lo tanto, terminaríamos cortos cuando revisemos todos la rebanada de bytes. Tendríamos que obtener más bytes aleatorios, "recursivamente". Y ahora incluso estamos perdiendo la ventaja de "llamada única al paquete rand " ...

Podríamos "algo" optimizar el uso de los datos aleatorios que adquirimos de math.Rand() . Podemos estimar cuántos bytes (bits) necesitaremos. 1 letra requiere bits de letterIdxBits , y necesitamos n letras, por lo que necesitamos n * letterIdxBits / 8.0 bytes redondeados. Podemos calcular la probabilidad de que un índice aleatorio no sea utilizable (ver más arriba), por lo que podríamos solicitar más que "más probablemente" será suficiente (si resulta que no lo es, repetimos el proceso). Podemos procesar el segmento de bytes como un "flujo de bits", por ejemplo, para lo cual tenemos una buena github.com/icza/bitio terceros: github.com/icza/bitio (revelación: soy el autor).

Pero el código de Benchmark todavía muestra que no estamos ganando. ¿Por que es esto entonces?

La respuesta a la última pregunta es porque rand.Read() usa un bucle y sigue llamando a Source.Int63() hasta que llena el segmento pasado. Exactamente lo que hace la solución RandStringBytesMaskImprSrc() , sin el búfer intermedio y sin la complejidad agregada. Es por eso que RandStringBytesMaskImprSrc() permanece en el trono. Sí, RandStringBytesMaskImprSrc() usa un rand.Source no sincronizado. rand.Source diferencia de rand.Read() . Pero el razonamiento todavía se aplica; y que está comprobado si usamos Rand.Read() lugar de rand.Read() (el primero tampoco está sincronizado).

II. Punto de referencia

Muy bien, vamos a comparar las diferentes soluciones.

BenchmarkRunes 1000000 1703 ns/op BenchmarkBytes 1000000 1328 ns/op BenchmarkBytesRmndr 1000000 1012 ns/op BenchmarkBytesMask 1000000 1214 ns/op BenchmarkBytesMaskImpr 5000000 395 ns/op BenchmarkBytesMaskImprSrc 5000000 303 ns/op

Simplemente cambiando de runas a bytes, inmediatamente tenemos un 22% de ganancia de rendimiento.

Deshacerse de rand.Intn() y usar rand.Int63() cambio da otro impulso de 24% .

El enmascaramiento (y la repetición en caso de grandes índices) se ralentiza un poco (debido a llamadas de repetición): -20% ...

Pero cuando hacemos uso de todos (o la mayoría) de los 63 bits aleatorios (10 índices de una llamada rand.Int63() ): eso acelera 3.4 veces .

Y, por último, si nos conformamos con un ( rand.Source ), rand.Source lugar de rand.Rand , nuevamente ganamos 23%.

Comparando la final con la solución inicial: RandStringBytesMaskImprSrc() es 5.6 veces más rápido que RandStringRunes() .


Si está dispuesto a agregar algunos caracteres a su grupo de caracteres permitidos, puede hacer que el código funcione con cualquier cosa que proporcione bytes aleatorios a través de un io.Reader. Aquí estamos utilizando crypto/rand .

// len(encodeURL) == 64. This allows (x <= 265) x % 64 to have an even // distribution. const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" // A helper function create and fill a slice of length n with characters from // a-zA-Z0-9_-. It panics if there are any problems getting random bytes. func RandAsciiBytes(n int) []byte { output := make([]byte, n) // We will take n bytes, one byte for each character of output. randomness := make([]byte, n) // read all random _, err := rand.Read(randomness) if err != nil { panic(err) } // fill output for pos := range output { // get random item random := uint8(randomness[pos]) // random % 64 randomPos := random % uint8(len(encodeURL)) // put into output output[pos] = encodeURL[randomPos] } return output }


Siguiendo icza''s solución maravillosamente explicada de icza''s , aquí hay una modificación que usa crypto/rand lugar de math/rand .

const ( letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // 52 possibilities letterIdxBits = 6 // 6 bits to represent 64 possibilities / indexes letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits ) func SecureRandomAlphaString(length int) string { result := make([]byte, length) bufferSize := int(float64(length)*1.3) for i, j, randomBytes := 0, 0, []byte{}; i < length; j++ { if j%bufferSize == 0 { randomBytes = SecureRandomBytes(bufferSize) } if idx := int(randomBytes[j%length] & letterIdxMask); idx < len(letterBytes) { result[i] = letterBytes[idx] i++ } } return string(result) } // SecureRandomBytes returns the requested number of bytes using crypto/rand func SecureRandomBytes(length int) []byte { var randomBytes = make([]byte, length) _, err := rand.Read(randomBytes) if err != nil { log.Fatal("Unable to generate random bytes") } return randomBytes }

Si desea una solución más genérica, que le permita pasar la porción de bytes de caracteres para crear la cadena, puede intentar usar esto:

// SecureRandomString returns a string of the requested length, // made from the byte characters provided (only ASCII allowed). // Uses crypto/rand for security. Will panic if len(availableCharBytes) > 256. func SecureRandomString(availableCharBytes string, length int) string { // Compute bitMask availableCharLength := len(availableCharBytes) if availableCharLength == 0 || availableCharLength > 256 { panic("availableCharBytes length must be greater than 0 and less than or equal to 256") } var bitLength byte var bitMask byte for bits := availableCharLength - 1; bits != 0; { bits = bits >> 1 bitLength++ } bitMask = 1<<bitLength - 1 // Compute bufferSize bufferSize := length + length / 3 // Create random string result := make([]byte, length) for i, j, randomBytes := 0, 0, []byte{}; i < length; j++ { if j%bufferSize == 0 { // Random byte buffer is empty, get a new one randomBytes = SecureRandomBytes(bufferSize) } // Mask bytes to get an index into the character slice if idx := int(randomBytes[j%length] & bitMask); idx < availableCharLength { result[i] = availableCharBytes[idx] i++ } } return string(result) }

Si desea pasar su propia fuente de aleatoriedad, sería trivial modificar lo anterior para aceptar un io.Reader lugar de usar crypto/rand .


Use el paquete uniuri , que genera cadenas criptográficamente seguras (no sesgadas) uniformes.


Usted puede simplemente escribir el código para ello. Este código puede ser un poco más simple si quiere confiar en que las letras son solo bytes cuando están codificadas en UTF-8.

package main import ( "fmt" "time" "math/rand" ) func init() { rand.Seed(time.Now().UnixNano()) } var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") func randSeq(n int) string { b := make([]rune, n) for i := range b { b[i] = letters[rand.Intn(len(letters))] } return string(b) } func main() { fmt.Println(randSeq(10)) }