play - random golang int




Golang gerador de números aleatórios como semear corretamente (4)

Eu estou tentando gerar uma string aleatória em Go e aqui está o código que escrevi até agora:

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

Minha implementação é muito lenta. A propagação usando o time traz o mesmo número aleatório por um certo tempo, então o loop itera repetidamente. Como posso melhorar meu código?


Cada vez que você define a mesma semente, você obtém a mesma sequência. Então, claro, se você está definindo a semente para o tempo em um loop rápido, você provavelmente vai chamá-lo com a mesma semente muitas vezes.

No seu caso, enquanto você está chamando sua função randInt até que você tenha um valor diferente, você está esperando o tempo (conforme retornado por Nano) mudar.

Quanto a todas as bibliotecas pseudo-aleatórias , você precisa definir a semente apenas uma vez, por exemplo, ao inicializar seu programa, a menos que você especificamente precise reproduzir uma determinada sequência (que geralmente é feita apenas para depuração e teste de unidade).

Depois disso, você simplesmente chama Intn para obter o próximo inteiro aleatório.

Mova a linha rand.Seed(time.Now().UTC().UnixNano()) da função randInt para o início da main e tudo será mais rápido.

Note também que eu acho que você pode simplificar o seu edifício de string:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UTC().UnixNano())
    fmt.Println(randomString(10))
}

func randomString(l int) string {
    bytes := make([]byte, l)
    for i := 0; i < l; i++ {
        bytes[i] = byte(randInt(65, 90))
    }
    return string(bytes)
}

func randInt(min int, max int) int {
    return min + rand.Intn(max-min)
}

Eu não entendo porque as pessoas estão semeando com um valor de tempo. Em minha experiência, isso nunca foi uma boa ideia. Por exemplo, enquanto o relógio do sistema é talvez representado em nanossegundos, a precisão do relógio do sistema não é nanossegundos.

Este programa não deve ser executado no playground Go, mas se você executá-lo em sua máquina, você terá uma estimativa aproximada sobre o tipo de precisão que você pode esperar. Eu vejo incrementos de cerca de 1000000 ns, então incrementos de 1 ms. São 20 bits de entropia que não são usados. Enquanto isso, os bits altos são quase sempre constantes.

O grau que isso importa para você irá variar, mas você pode evitar armadilhas de valores de semente baseados no clock simplesmente usando o crypto/rand.Read como fonte para sua semente. Ele lhe dará aquela qualidade não-determinística que você provavelmente está procurando em seus números aleatórios (mesmo que a própria implementação esteja limitada a um conjunto de seqüências aleatórias distintas e determinísticas).

import (
    crypto_rand "crypto/rand"
    "encoding/binary"
    math_rand "math/rand"
)

func init() {
    var b [8]byte
    _, err := crypto_rand.Read(b[:])
    if err != nil {
        panic("cannot seed math/rand package with cryptographically secure random number generator")
    }
    math_rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
}

Como uma nota lateral, mas em relação à sua pergunta. Você pode criar seu próprio rand.Source usando este método para evitar o custo de ter bloqueios protegendo a fonte. As funções de utilidade do pacote rand são convenientes, mas também usam travas sob o capô para evitar que a fonte seja usada simultaneamente. Se você não precisa disso, pode evitá-lo criando sua própria Source e usá-la de maneira não concorrente. Independentemente disso, você NÃO deve estar propagando seu gerador de números aleatórios entre iterações, nunca foi projetado para ser usado dessa maneira.


São nano segundos, quais são as chances de conseguir a mesma semente duas vezes.
De qualquer forma, obrigado pela ajuda, aqui está a minha solução final com base em todas as entradas.

package main

import (
    "math/rand"
    "time"
)

func init() {
    rand.Seed(time.Now().UTC().UnixNano())
}

// generates a random string
func srand(min, max int, readable bool) string {

    var length int
    var char string

    if min < max {
        length = min + rand.Intn(max-min)
    } else {
        length = min
    }

    if readable == false {
        char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    } else {
        char = "ABCDEFHJLMNQRTUVWXYZabcefghijkmnopqrtuvwxyz23479"
    }

    buf := make([]byte, length)
    for i := 0; i < length; i++ {
        buf[i] = char[rand.Intn(len(char)-1)]
    }
    return string(buf)
}

// For testing only
func main() {
    println(srand(5, 5, true))
    println(srand(5, 5, true))
    println(srand(5, 5, true))
    println(srand(5, 5, false))
    println(srand(5, 7, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 10, false))
    println(srand(5, 50, true))
    println(srand(5, 4, true))
    println(srand(5, 400, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
    println(srand(6, 5, true))
}

Se o seu objetivo é apenas gerar uma picada de número aleatório, então eu acho desnecessário complicá-lo com múltiplas chamadas de função ou resetar a semente toda vez.

O passo mais importante é chamar a função seed apenas uma vez antes de executar rand.Init(x) . Seed usa o valor de semente fornecido para inicializar a Origem padrão para um estado determinístico. Então, seria sugerido chamá-lo uma vez antes da chamada de função real para o gerador de números pseudo-aleatórios.

Aqui está um código de exemplo, criando uma seqüência de números aleatórios

package main 
import (
    "fmt"
    "math/rand"
    "time"
)



func main(){
    rand.Seed(time.Now().UnixNano())

    var s string
    for i:=0;i<10;i++{
    s+=fmt.Sprintf("%d ",rand.Intn(7))
    }
    fmt.Printf(s)
}

O motivo pelo qual usei o Sprintf é porque permite a formatação simples de strings.

Além disso, em rand.Intn(7) Intn retorna, como um int, um número pseudo-aleatório não negativo em [0,7].





go