Я пытаюсь сгенерировать случайную строку в 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
возвращает одно и то же случайное число в течение определенного времени, поэтому цикл повторяется снова и снова. Как я могу улучшить свой код?
Ответы:
Каждый раз, когда вы устанавливаете одно и то же семя, вы получаете одну и ту же последовательность. Поэтому, конечно, если вы устанавливаете начальное время в быстрый цикл, вы, вероятно, будете вызывать его с одним и тем же начальным числом много раз.
В вашем случае, когда вы вызываете свою
randInt
функцию до тех пор, пока не получите другое значение, вы ждете, пока изменится время (возвращаемое Nano).Как и для всех псевдослучайных библиотек , вы должны установить начальное значение только один раз, например, при инициализации вашей программы, если только вам не нужно специально воспроизвести заданную последовательность (что обычно делается только для отладки и модульного тестирования).
После этого вы просто звоните,
Intn
чтобы получить следующее случайное число.Переместите
rand.Seed(time.Now().UTC().UnixNano())
строку из функции randInt в начало основной, и все будет быстрее.Обратите внимание, что я думаю, вы можете упростить сборку строк:
источник
rand.Seed(...)
в функциюinit()
.init()
вызывается автоматически раньшеmain()
. Обратите внимание, что вам не нужно звонитьinit()
сmain()
!math/rand
любом случае не является криптографически защищенным. Если это требование,crypto/rand
следует использовать.Я не понимаю, почему люди сеют с ценностью времени. По моему опыту, это никогда не было хорошей идеей. Например, хотя системные часы могут быть представлены в наносекундах, точность тактовой частоты системы не равна наносекундам.
Эту программу не следует запускать на игровой площадке Go, но если вы запустите ее на своем компьютере, вы получите приблизительную оценку того, какую точность вы можете ожидать. Я вижу приращения около 1000000 нс, поэтому приращение составляет 1 мс. Это 20 битов энтропии, которые не используются. Все время старшие биты в основном постоянны.
Степень, в которой это важно для вас, будет разной, но вы можете избежать ловушек значений начальных значений на основе тактовой частоты, просто используя в
crypto/rand.Read
качестве источника начальное значение. Это даст вам то недетерминированное качество, которое вы, вероятно, ищете в своих случайных числах (даже если сама фактическая реализация ограничена набором отдельных и детерминированных случайных последовательностей).Как примечание стороны, но по отношению к вашему вопросу. Вы можете создать свой собственный,
rand.Source
используя этот метод, чтобы избежать затрат на блокировку источника. Функцииrand
утилиты пакета удобны, но они также используют блокировки под капотом для предотвращения одновременного использования источника. Если вам это не нужно, вы можете избежать этого, создав собственныйSource
и использовать его не одновременно. В любом случае, вы НЕ должны перезаряжать генератор случайных чисел между итерациями, он никогда не был разработан для такого использования.источник
просто чтобы выбросить это для потомков: иногда может быть предпочтительнее генерировать случайную строку, используя исходную строку набора символов. Это полезно, если строка должна быть введена человеком вручную; Исключение 0, O, 1 и l может помочь уменьшить ошибку пользователя.
и я обычно устанавливаю семя внутри
init()
блока. Они задокументированы здесь: http://golang.org/doc/effective_go.html#initисточник
-1
вrand.Intn(len(alpha)-1)
. Это потому, чтоrand.Intn(n)
всегда возвращает число, которое меньшеn
(другими словами: от нуля доn-1
включительно).-1
inlen(alpha)-1
гарантировало бы, что число 9 никогда не использовалось в последовательности.ОК, почему так сложно!
Это основано на коде дистроя, но подходит для моих нужд.
Это умирает шесть (рандов целых
1 =< i =< 6
)Функция выше - это то же самое.
Я надеюсь, что эта информация была полезна.
источник
3 5 2 5 4 2 5 6 3 1
rand.Intn()
, в противном случае вы всегда будете получать один и тот же номер при каждом запуске вашей программы.var bytes int
? Какая разница для изменения выше ,bytes = rand.Intn(6)+1
чтобыbytes := rand.Intn(6)+1
? Они оба, кажется, работают для меня, является ли один из них неоптимальным по какой-то причине?Это нано секунды, каковы шансы получить одно и то же семя дважды.
В любом случае, спасибо за помощь, вот мое конечное решение, основанное на всех входах.
источник
what are the chances of getting the exact the exact same [nanosecond] twice?
Отлично. Все зависит от внутренней точности реализации времени выполнения golang. Даже если единицы измерения - это наносекунды, наименьшее приращение может составлять миллисекунды или даже секунды.Если ваша цель состоит в том, чтобы просто генерировать жало случайного числа, то я думаю, что нет необходимости усложнять его множественными вызовами функций или каждый раз сбрасывая начальное число.
Самый важный шаг - вызвать функцию seed только один раз перед тем, как она будет запущена
rand.Init(x)
. Seed использует предоставленное начальное значение для инициализации источника по умолчанию в детерминированном состоянии. Таким образом, было бы предложено вызвать его один раз перед фактическим вызовом функции для генератора псевдослучайных чисел.Вот пример кода, создающего строку случайных чисел
Причина, по которой я использовал Sprintf, заключается в том, что он позволяет простое форматирование строк.
Кроме того, In
rand.Intn(7)
Intn возвращает в качестве целого неотрицательное псевдослучайное число в [0,7).источник
@ [Денис Сегюре] опубликовал сообщение правильно. Но в моем случае мне нужно новое семя каждый раз, поэтому ниже код;
Incase вам нужны быстрые функции. Я использую как это.
источник
источник
Небольшое обновление из-за изменения api golang, пожалуйста, опустите .UTC ():
time.Now (). UTC () .UnixNano () -> time.Now (). UnixNano ()
источник