Генератор случайных чисел Голанга, как правильно семени

Я пытаюсь создать случайную строку в Go, и вот код, который я написал до сих пор:

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

Моя реализация выполняется очень медленно. Сеяние с использованием time приводит к одному и тому же случайному числу в течение определенного времени, поэтому цикл повторяется снова и снова. Как я могу улучшить свой код?

Ответ 1

Каждый раз, когда вы устанавливаете одно и то же семя, вы получаете одну и ту же последовательность. Поэтому, конечно, если вы устанавливаете начальное время в быстрый цикл, вы, вероятно, будете вызывать его с одним и тем же начальным числом много раз.

В вашем случае, когда вы вызываете функцию randInt до тех пор, пока не получите другое значение, вы ждете, пока изменится время (возвращаемое Nano).

Как и для всех псевдослучайных библиотек, вы должны установить начальное значение только один раз, например, при инициализации вашей программы, если только вам не нужно специально воспроизвести заданную последовательность (что обычно делается только для отладки и модульного тестирования).

После этого вы просто вызываете Intn чтобы получить следующее случайное целое число.

Переместите rand.Seed(time.Now().UTC().UnixNano()) из функции randInt в начало главной, и все будет быстрее.

Обратите внимание, что я думаю, вы можете упростить сборку строк:

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

Ответ 2

просто выбросить его для потомков: иногда бывает предпочтительнее генерировать случайную строку с использованием строки исходного набора символов. Это полезно, если строка должна вводиться вручную человеком; исключая 0, O, 1 и l, может помочь уменьшить ошибку пользователя.

var alpha = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"

// generates a random string of fixed size
func srand(size int) string {
    buf := make([]byte, size)
    for i := 0; i < size; i++ {
        buf[i] = alpha[rand.Intn(len(alpha))]
    }
    return string(buf)
}

и я обычно устанавливаю семя внутри блока init(). Они описаны здесь: http://golang.org/doc/effective_go.html#init

Ответ 3

Хорошо, почему так сложно!

package main

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

func main() {
    rand.Seed( time.Now().UnixNano())
    var bytes int

    for i:= 0 ; i < 10 ; i++{ 
        bytes = rand.Intn(6)+1
        fmt.Println(bytes)
        }
    //fmt.Println(time.Now().UnixNano())
}

Это основано на коде dystroy, но для моих нужд.

Он умирает шесть (rands ints 1 =< i =< 6)

func randomInt (min int , max int  ) int {
    var bytes int
    bytes = min + rand.Intn(max)
    return int(bytes)
}

Вышеуказанная функция - это то же самое.

Я надеюсь, что эта информация была полезной.

Ответ 4

Я не понимаю, почему люди сеют с ценностью времени. По моему опыту, это никогда не было хорошей идеей. Например, хотя системные часы могут быть представлены в наносекундах, точность системных часов не равна наносекундам.

Эту программу не следует запускать на игровой площадке Go, но если вы запустите ее на своем компьютере, вы получите приблизительную оценку того, какую точность вы можете ожидать. Я вижу приращения около 1000000 нс, поэтому приращение составляет 1 мс. Это 20 битов энтропии, которые не используются. Все время старшие биты в основном постоянны.

Степень, в которой это важно для вас, будет разной, но вы можете избежать ловушек значений начальных значений на основе тактовой частоты, просто используя crypto/rand.Read качестве источника для вашего начального числа. Это даст вам то недетерминированное качество, которое вы, вероятно, ищете в своих случайных числах (даже если сама фактическая реализация ограничена набором отдельных и детерминированных случайных последовательностей).

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[:])))
}

Как примечание стороны, но по отношению к вашему вопросу. Вы можете создать свой собственный rand.Source используя этот метод, чтобы избежать затрат на блокировку, защищающую источник. Функции утилиты rand package удобны, но они также используют блокировки под капотом, чтобы предотвратить одновременное использование источника. Если вам это не нужно, вы можете избежать этого, создав собственный Source и использовать его не одновременно. В любом случае, вам НЕ следует перезаряжать генератор случайных чисел между итерациями, он никогда не был разработан для такого использования.

Ответ 5

Это nano секунд, каковы шансы получить одно и то же семя дважды.
В любом случае, спасибо за помощь, вот мое конечное решение, основанное на всех входах.

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

Ответ 6

Если ваша цель состоит в том, чтобы просто генерировать жало случайного числа, то я думаю, что нет необходимости усложнять его множественными вызовами функций или каждый раз сбрасывать начальное число.

Самый важный шаг - вызвать функцию seed только один раз, прежде чем запустить rand.Init(x). Seed использует предоставленное начальное значение для инициализации источника по умолчанию в детерминированном состоянии. Таким образом, было бы предложено вызвать его один раз перед фактическим вызовом функции для генератора псевдослучайных чисел.

Вот пример кода, создающего строку случайных чисел

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

Причина, по которой я использовал Sprintf, заключается в том, что он допускает простое форматирование строк.

Кроме того, в rand.Intn(7) Intn возвращает, как int, неотрицательное псевдослучайное число в [0,7).

Ответ 7

Небольшое обновление из-за изменения golang api, пожалуйста, опустите .UTC():

time.Now(). UTC().UnixNano() → time.Now(). UnixNano()

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

func main() {
    rand.Seed(time.Now().UnixNano())
    fmt.Println(randomInt(100, 1000))
}

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