Golang генератор случайных чисел

В следующей инструкции показано, как написать программу для генерации случайных чисел. Данную функциональность предоставляет пакет math/rand . Случайные числа, сгенерированные через math/rand считаются криптографически ненадежными , так как из-за использования определенного сида последовательности повторяются.

Для генерации криптографически надежных чисел лучше использовать пакет crypto/rand , где последовательности не повторяются.

Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎

Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.

Как генерировать случайные числа в Golang?

1. Создайте файл rand.go со следующим содержимым:

2. Запустите код в терминале через go run rand.go ;
3. Посмотрите на вывод:

В предыдущем коде показано два варианта генерации случайных чисел. Первый вариант использует пакет math/rand , что криптографически ненадежен . Он позволяет генерировать одинаковую последовательность с использованием Source с тем же номером сида. Данный подход обычно используется в тестах. Причина этого заключается в воспроизводимости последовательности.

Второй вариант является криптографически надежным , он использует пакет crypto/rand . API использует Reader для предоставления экземпляра криптографически сильного генератора псевдо-случайных чисел. Данный пакет сам по себе имеет свой Reader по умолчанию, который основан на системном генераторе случайных чисел.

Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.

E-mail: vasile.buldumac@ati.utm.md


Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники

  • 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
  • 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»

Generate Random number in Go (Golang)


Go provide a ‘math/rand’ package which has inbuilt support for generating pseudo-random numbers. This package defines methods which can be used to generate

  • A pseudo-random number within the range from 0 to n
  • A pseudo-random number without range specified. The range will depend upon the type of int i.e int64, int32, uint64, etc

What is a pseudo-random number

Before proceeding let’s first understand what pseudo-random number means. Pseudo-random number is not truly random as its value is completed determined by the initial value known as seed.

To understand the role of seed, let’s first look at the very basic function which can generate a random number in range [0, n). The below function in the rand package can be used to generate pseudo-random number in range [0, n). Bracket at the end in [0,n) means that n is exclusive.

The above function returns an int value between 0 to n. Let’s write a program without seed value. We have passed 10, so below function will generate random numbers in range [0,10)

Try running above the program multiple times. It will give the same output every time. On my system, it gives below output

Now let’s try running the same program but first providing seed value.

We are giving seed value as number of seconds which has elapsed till January 1, 1970, UTC.

It gives different output each time you execute this program as seed value is different. That is what is meant when we say that go generates pseudo-random numbers.

Random Generator Functions in rand package

Now we have understood what pseudo-random number generation, let’s look at some of the function provided by the rand package for generation of random numbers. You can use any of these functions to generate a random number based on your requirements.

Читайте также: Ремень генератора то дымит то нет

Pseudo-Random Number Generator Functions with range.

All functions take n as an argument and will panic if n Pseudo Random Number Generator Functions without range.

Generating random numbers over a range in Go

All the integer functions in math/rand generate non-negative numbers.

I would like to generate random numbers in the range [-m, n). In other words, I would like to generate a mix of positive and negative numbers.

5 Answers 5

I found this example at Go Cookbook, which is equivalent to rand.Range(min, max int) (if that function existed):

Don’t forget to seed the PRNG before calling any rand function.

This will generate random numbers within given range [a, b]

As to prevent repeating min and max over and over again, I suggest to switch range and random in thinking about it. This is what I found to work as expected:

Specifying the range

The solution you found in the Cookbook misses to exactly specify how min and max work, but of course it meets your specification ([-min, max)). I decided to specify the range as a closed interval ([-min, max], that means its borders are included in the valid range). Compared to my understanding of the Cookbook description:

gives you that random number within any two positive numbers that you specify (in this case, 1 and 6).

the Cookbook implementation is off by one (which of course brings it in good company with lots of programs that are helpful at first glance).

How to properly seed random number generator

I am trying to generate a random string in Go and here is the code I have written so far:

My implementation is very slow. Seeding using time brings the same random number for a certain time, so the loop iterates again and again. How can I improve my code?

10 Answers 10

Each time you set the same seed, you get the same sequence. So of course if you’re setting the seed to the time in a fast loop, you’ll probably call it with the same seed many times.

In your case, as you’re calling your randInt function until you have a different value, you’re waiting for the time (as returned by Nano) to change.

As for all pseudo-random libraries, you have to set the seed only once, for example when initializing your program unless you specifically need to reproduce a given sequence (which is usually only done for debugging and unit testing).

After that you simply call Intn to get the next random integer.

Move the rand.Seed(time.Now().UTC().UnixNano()) line from the randInt function to the start of the main and everything will be faster. And lose the .UTC() call since:

UnixNano returns t as a Unix time, the number of nanoseconds elapsed since January 1, 1970 UTC.

Note also that I think you can simplify your string building:

I don’t understand why people are seeding with a time value. This has in my experience never been a good idea. For example, while the system clock is maybe represented in nanoseconds, the system’s clock precision isn’t nanoseconds.

This program should not be run on the Go playground but if you run it on your machine you get a rough estimate on what type of precision you can expect. I see increments of about 1000000 ns, so 1 ms increments. That’s 20 bits of entropy that are not used. All the while the high bits are mostly constant!? Roughly

Читайте также: Ремень генератора авео 1 2 sohc

24 bits of entropy over a day which is very brute forceable (which can create vulnerabilities).

The degree that this matters to you will vary but you can avoid pitfalls of clock based seed values by simply using the crypto/rand.Read as source for your seed. It will give you that non-deterministic quality that you are probably looking for in your random numbers (even if the actual implementation itself is limited to a set of distinct and deterministic random sequences).

As a side note but in relation to your question. You can create your own rand.Source using this method to avoid the cost of having locks protecting the source. The rand package utility functions are convenient but they also use locks under the hood to prevent the source from being used concurrently. If you don’t need that you can avoid it by creating your own Source and use that in a non-concurrent way. Regardless, you should NOT be reseeding your random number generator between iterations, it was never designed to be used that way.

Edit: I used to work in ITAM/SAM and the client we built (then) used a clock based seed. After a Windows update a lot of machines in the company fleet rebooted at roughly the same time. This caused an involtery DoS attack on upstream server infrastructure because the clients was using system up time to seed randomness and these machines ended up more or less randomly picking the same time slot to report in. They were meant to smear the load over a period of an hour or so but that did not happen. Seed responsbily!

Быстрый генератор псевдослучайных чисел на Go

Продолжаем знакомство с Go. Первая статья должна была заставить читателей начать писать веб-приложения на Go. В этой статье будет показано, как с помощью простых инструментов Go можно создавать высокопроизводительный код под многоядерные системы.

Для чего нужны псевдослучайные числа

Псевдослучайные числа достаточно широко применяются при разработке программ. Наиболее часто их используют в вероятностных алгоритмах. Например, для выборки фиксированного количества случайных значений из бесконечного ряда aka reservoir sampling. Этот матан используется для построения в режиме онлайн гистограмм (aka percentiles) по времени выполнения запроса либо по размеру ответа. Такие данные дают намного больше информации по сравнению со средними значениями и экстремумами при мониторинге и анализе производительности программ.

Псевдослучайные числа в Go

В стандартную поставку Go входит пакет math/rand, который предоставляет функциональность для генерации псевдослучайных чисел. Например, чтобы получить псевдослучайное число от 0 до N-1, достаточно вызвать функцию rand.Intn. Это потокобезопасная функция — ее можно вызывать из нескольких одновременно запущенных потоков. Но есть одна проблема: скорость ее работы не растет при увеличении количества ядер CPU и даже наоборот — падает в несколько раз:

После названия бенчмарка указано количество ядер CPU, на котором был запущен бенчмарк. Третья колонка — время, затраченное на один вызов функции. Видно, что быстрее всего rand.Int31n работает на одном ядре — около 30 млн вызовов в секунду. На четырех ядрах суммарная скорость снижается до 8 млн вызовов в секунду. Это связано с тем, что «под капотом» rand.Int31n используется стандартный мьютекс, который по определению рубит масштабируемость на корню.

Сейчас уже используются сервера с 64 ядрами и более. Как же получить максимальную производительность генератора псевдослучайных чисел на многоядерном сервере? Стандартный ответ «использовать локальные ГПСЧ для каждого ядра CPU». Номер ядра, на котором исполняется текущий поток, можно узнать с помощью функции getcpu. Но тут есть несколько препятствий:

  1. Вызов getcpu занимает ненулевое время, которое может превысить время, необходимое для генерации следующего псевдослучайного числа.
  2. Getcpu может вернуть некорректный номер ядра, если операционная система решит перенести текущий поток на другое ядро во время вызова getcpu. Поэтому локальный генератор для каждого ядра CPU должен быть защищен мьютексом. Это тоже увеличивает время, необходимое на генерацию числа.

Читайте также: Форд орион генератор дизель

Может, есть решение получше? Например, использовать thread local storage для хранения отдельных ГПСЧ на каждый поток. Не выйдет по следующим причинам:

  1. Go не предоставляет доступ к потокам операционной системы. В Go есть только горутины, которые исполняются на потоках операционной системы.
  2. Стандартная библиотека Go не предоставляет API для управления thread local storage или goroutine local storage.

Есть еще один вариант — сделать массив ГПСЧ, защищенных отдельными мьютексами, и обращаться к ним последовательно через глобальный атомарно инкрементируемый индекс. Вот результаты бенчмарков для такого варианта:

Производительность на одном ядре почти в два раза ниже, чем в предыдущем бенчмарке. Это объясняется дополнительными накладными расходами на атомарный инкремент глобального индекса. Зато на четырех ядрах этот вариант опережает предыдущий в 3 раза. Но итоговая производительность бенчмарка на четырех ядрах все равно ниже производительности предыдущего варианта на одном ядре.

Если количество горутин, генерирующих случайные числа, ограничено и постоянно во времени, то можно завести в каждой такой горутине свой ГПСЧ, чтобы получить максимальную производительность и масштабируемость. Но это не всегда возможно. Например, веб-сервер обрабатывает каждый входящий запрос в отдельной горутине. Таким образом, количество горутин зависит от текущей нагрузки на сервер и его сложно контролировать из обработчика запросов.

Существует ли более скоростной и масштабируемый вариант? Да!

Масштабируемый ГПСЧ на sync.Pool

В стандартной библиотеке Go есть классная штука — sync.Pool. Это хранилище повторно используемых объектов, куда можно складывать неприменяемые объекты, чтобы кто-то другой смог их достать и повторно использовать. sync.Pool оптимизирован под использование на многоядерных компьютерах. Что если хранить набор ГПСЧ в sync.Pool, доставая их оттуда для генерации следующего псевдослучайного числа? Смотрим результаты бенчмарков:

Как видим, скорость ГПСЧ растет с увеличением количества ядер. На четырех ядрах удается достичь 70 млн вызовов в секунду. Это лучше первого варианта в 8 раз и лучше второго варианта в 3 раза.

Кто-то может подумать, что ради достижения такой производительности пришлось пожертвовать удобством API. Нет, API — простое, как грабли: вызываешь функцию fastrand.Int32n(N) — получаешь псевдослучайное число в диапазоне от 0 до N-1. Данная функция потокобезопасна — ее можно вызывать из параллельно работающих потоков.

Кто-то заподозрит, что пришлось пожертвовать качеством кода в угоду производительности. Вроде код выглядит нормально. Привожу полный исходный код пакета fastrand:

Исходники всех бенчмарков, рассмотренных выше, находятся в файле fastrand_timing_test.go. Чтобы запустить эти бенчмарки, достаточно выполнить две команды:

Первая команда скачает исходники fastrand в папку $GOPATH/src, вторая — запустит все бенчмарки, находящиеся в исходниках fastrand.

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

Бенчмарки с тестами на Go писать очень просто. Для этого достаточно прочесть краткую документацию к пакету testing из стандартной библиотеки Go.


Разработка быстрых генераторов псевдослучайных чисел может быть простой и интересной. Особенно, если использовать Go 🙂

Go идеально подходит для создания высокопроизводительного кода под многоядерные компьютеры. В Go минимум бесполезных абстракций и головоломных конструкций. Благодаря этому код на Go легко написать и легко понять. Мы это увидели на наглядном примере. Пакет fastrand успешно используется в наших высоконагруженных сервисах.

Сомневающимся предлагаю написать аналог fastrand с таким же удобным API и с такой же масштабируемостью на другом языке программирования. Жду ссылки на эти проекты в комментариях к статье.

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

  • Свежие записи
    • Как я ремонтировала свой автомобиль
    • Автомобильные зеркала
    • Ностальгия по «бугатти»
    • Тест драйв. OPEL MOKKA – лучший полноприводный кроссовер в своем классе
    • McFarlan — от рассвета до заката