Estoy tratando de generar una cadena aleatoria en Go y aquí está el código que he escrito hasta ahora:
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)
}
Mi implementación es muy lenta. Sembrar usando time
trae el mismo número aleatorio durante cierto tiempo, por lo que el ciclo se repite una y otra vez. ¿Cómo puedo mejorar mi código?
Respuestas:
Cada vez que establece la misma semilla, obtiene la misma secuencia. Entonces, por supuesto, si está configurando la semilla en el tiempo en un ciclo rápido, probablemente la llame con la misma semilla muchas veces.
En su caso, mientras llama a su
randInt
función hasta que tenga un valor diferente, está esperando que cambie el tiempo (tal como lo devolvió Nano).Como para todas las bibliotecas pseudoaleatorias , debe establecer la semilla solo una vez, por ejemplo, al inicializar su programa, a menos que necesite reproducir específicamente una secuencia dada (que generalmente solo se realiza para la depuración y la prueba de la unidad).
Después de eso, simplemente llame
Intn
para obtener el siguiente entero aleatorio.Mueva la
rand.Seed(time.Now().UTC().UnixNano())
línea desde la función randInt hasta el inicio de main y todo será más rápido.Tenga en cuenta también que creo que puede simplificar su construcción de cadenas:
fuente
rand.Seed(...)
a la funcióninit()
.init()
se llama automáticamente antesmain()
. Tenga en cuenta que no necesita llamarinit()
desdemain()
!math/rand
no es criptográficamente seguro de todos modos. Si eso es un requisito,crypto/rand
debe usarse.No entiendo por qué las personas están sembrando con un valor de tiempo. En mi experiencia, esto nunca ha sido una buena idea. Por ejemplo, mientras que el reloj del sistema puede estar representado en nanosegundos, la precisión del reloj del sistema no es nanosegundos.
Este programa no debe ejecutarse en el patio de juegos Go, pero si lo ejecuta en su máquina obtendrá una estimación aproximada de qué tipo de precisión puede esperar. Veo incrementos de aproximadamente 1000000 ns, por lo que incrementos de 1 ms. Son 20 bits de entropía que no se usan. Todo el tiempo los bits altos son en su mayoría constantes.
El grado que esto le importa variará, pero puede evitar las trampas de los valores de semilla basados en el reloj simplemente usando la
crypto/rand.Read
fuente como fuente para su semilla. Le dará esa calidad no determinista que probablemente esté buscando en sus números aleatorios (incluso si la implementación real en sí misma se limita a un conjunto de secuencias aleatorias distintas y deterministas).Como nota al margen pero en relación con su pregunta. Puede crear el suyo
rand.Source
usando este método para evitar el costo de tener bloqueos que protegen la fuente. Lasrand
funciones de la utilidad del paquete son convenientes, pero también usan bloqueos debajo del capó para evitar que la fuente se use simultáneamente. Si no lo necesita, puede evitarlo creando el suyo propioSource
y utilizándolo de manera no concurrente. De todos modos, NO debe volver a sembrar su generador de números aleatorios entre iteraciones, nunca fue diseñado para usarse de esa manera.fuente
solo para tirarlo a la posteridad: a veces puede ser preferible generar una cadena aleatoria usando una cadena de juego de caracteres inicial. Esto es útil si se supone que la cadena debe ser ingresada manualmente por un humano; excluir 0, O, 1 yl puede ayudar a reducir el error del usuario.
y típicamente pongo la semilla dentro de un
init()
bloque. Están documentados aquí: http://golang.org/doc/effective_go.html#initfuente
-1
enrand.Intn(len(alpha)-1)
. Esto se debe a querand.Intn(n)
siempre devuelve un número menor quen
(en otras palabras: de cero an-1
inclusivo).-1
enlen(alpha)-1
habría garantizado que el número 9 nunca se usó en la secuencia.OK por qué tan complejo!
Esto se basa en el código de la distribución pero se ajusta a mis necesidades.
Son los seis (rands ints
1 =< i =< 6
)La función anterior es exactamente la misma cosa.
Espero que esta información haya sido de utilidad.
fuente
3 5 2 5 4 2 5 6 3 1
rand.Intn()
, de lo contrario siempre obtendrá el mismo número cada vez que ejecute su programa.var bytes int
? ¿Cuál es la diferencia para cambiar lo anteriorbytes = rand.Intn(6)+1
abytes := rand.Intn(6)+1
? Ambos parecen funcionar para mí, ¿es uno de ellos subóptimo por alguna razón?Son nano segundos, ¿cuáles son las posibilidades de obtener la misma semilla dos veces?
De todos modos, gracias por la ayuda, aquí está mi solución final basada en todos los aportes.
fuente
what are the chances of getting the exact the exact same [nanosecond] twice?
excelente. Todo depende de la precisión interna de la implementación de los tiempos de ejecución de golang. Aunque las unidades son nano-segundos, el incremento más pequeño podría ser un milisegundo o incluso un segundo.Si su objetivo es solo generar una picadura de número aleatorio, entonces creo que no es necesario complicarlo con llamadas a múltiples funciones o restablecer la semilla cada vez.
El paso más importante es llamar a la función semilla solo una vez antes de ejecutarse realmente
rand.Init(x)
. La semilla utiliza el valor de semilla proporcionado para inicializar la fuente predeterminada a un estado determinista. Por lo tanto, se sugiere llamarlo una vez antes de la llamada a la función real al generador de números pseudoaleatorios.Aquí hay un código de muestra que crea una cadena de números aleatorios
La razón por la que usé Sprintf es porque permite un formato de cadena simple.
Además, In
rand.Intn(7)
Intn devuelve, como int, un número pseudoaleatorio no negativo en [0,7].fuente
@ [Denys Séguret] ha publicado correctamente. Pero en mi caso, necesito nuevas semillas cada vez, por lo tanto, debajo del código;
En caso de que necesite funciones rápidas. Yo uso así.
fuente
fuente
Pequeña actualización debido al cambio de la API de Golang, omita .UTC ():
Ahora(). UTC () .UnixNano () -> time.Now (). UnixNano ()
fuente