Как использовать seed в любом генераторе
Перейти к содержимому

Как использовать seed в любом генераторе

  • автор:

How do I Seed random number generator on official Tour of Go?

The official tour of Go gives the following code in the sandbox:

And this instruction:

Note: the environment in which these programs are executed is deterministic, so each time you run the example program rand.Intn will return the same number. (To see a different number, seed the number generator; see rand.Seed.)

After reading the entry under the official documentation for rand.Seed and reading this answer, I still can’t correctly seed the random number generator.

Can someone please demonstrate how the rand.Seed function should be used to seed the random number generator?

Many thanks, Jon

3 Answers 3

By default rand.Intn uses the globalRand.Intn. Its created internally, refer here. So when you set via rand.Seed

Then globalRand uses the new seed value.

When needed you can create your own rand generator with seed value. Refer to godoc example.

EDIT:

As @AlexanderTrakhimenok mentioned, in playground program execution is deterministic . However, the playground doesn’t stop you from supplying rand.Seed value.

Remember Seed value is int64 .

When you rand.Intn , it uses default seed value 1 for globalRand .

And in playground time.Now().UTC().UnixNano() gives you same value 1257894000000000000 since the start time is locked to a constant . But it is different from default seed value, that’s why second playground link produces the different result.

So above two would produce the same result always.

How should we change the result in playground?

Yes, we can. Let’s supply UnixNano() value 1500909006430687579 to rand.Seed , which is generated from my machine.

Abhishek K's user avatar

As you quoted yourself:

the environment in which these programs are executed is deterministic.

So the Go Playground by design does not allow to create truly pseudo-random outputs.

This is done intentionally for purpose of caching results to minimize CPU/memory usage for consequent runs. So the engine can evaluate your program just once and serve the same cached output every time when you or anyone else run it again.

For the same purpose the start time is locked to a constant.

You may want to read a blog post on how and why it’s implemented this way: https://blog.golang.org/playground

For anyone who really needs a pseudo-random seed in go playground, just don’t use the constant timestamp. You can access the execution memory usage which is slightly different on each execution in go playground even in the sandbox. So your seed can very well be non-deterministic and actually somewhat pseudo-random like this:

Maybe this is also a good idea if you fear that someone is tempering with your system clock to break your randomness. However that is just a more safe but still a pseudo-randomness.

If you have this kind of thoughts you might want to use cryptography safe random numbers instead 🙂

Python 3: Генерация случайных чисел (модуль random)¶

Python порождает случайные числа на основе формулы, так что они не на самом деле случайные, а, как говорят, псевдослучайные [1]. Этот способ удобен для большинства приложений (кроме онлайновых казино) [2].

[1] Википедия: Генератор псевдослучайных чисел
[2] Доусон М. Программируем на Python. — СПб.: Питер, 2014. — 416 с.: ил. — 3-е изд

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

random.random¶

random.random() — возвращает псевдослучайное число от 0.0 до 1.0

random.seed¶

random.seed(<Параметр>) — настраивает генератор случайных чисел на новую последовательность. По умолчанию используется системное время. Если значение параметра будет одиноким, то генерируется одинокое число:

random.uniform¶

random.uniform(<Начало>, <Конец>) — возвращает псевдослучайное вещественное число в диапазоне от <Начало> до <Конец> :

random.randint¶

random.randint(<Начало>, <Конец>) — возвращает псевдослучайное целое число в диапазоне от <Начало> до <Конец> :

random.choince¶

random.choince(<Последовательность>) — возвращает случайный элемент из любой последовательности (строки, списка, кортежа):

random.randrange¶

random.randrange(<Начало>, <Конец>, <Шаг>) — возвращает случайно выбранное число из последовательности.

random.shuffle¶

random.shuffle(<Список>) — перемешивает последовательность (изменяется сама последовательность). Поэтому функция не работает для неизменяемых объектов.

Вероятностные распределения¶

random.triangular(low, high, mode) — случайное число с плавающей точкой, low ≤ N ≤ high . Mode — распределение.

random.betavariate(alpha, beta) — бета-распределение. alpha>0 , beta>0 . Возвращает от 0 до 1.

random.expovariate(lambd) — экспоненциальное распределение. lambd равен 1/среднее желаемое. Lambd должен быть отличным от нуля. Возвращаемые значения от 0 до плюс бесконечности, если lambd положительно, и от минус бесконечности до 0, если lambd отрицательный.

random.gammavariate(alpha, beta) — гамма-распределение. Условия на параметры alpha>0 и beta>0 .

random.gauss(значение, стандартное отклонение) — распределение Гаусса.

random.lognormvariate(mu, sigma) — логарифм нормального распределения. Если взять натуральный логарифм этого распределения, то вы получите нормальное распределение со средним mu и стандартным отклонением sigma . mu может иметь любое значение, и sigma должна быть больше нуля.

random.normalvariate(mu, sigma) — нормальное распределение. mu — среднее значение, sigma — стандартное отклонение.

random.vonmisesvariate(mu, kappa) — mu — средний угол, выраженный в радианах от 0 до 2π, и kappa — параметр концентрации, который должен быть больше или равен нулю. Если каппа равна нулю, это распределение сводится к случайному углу в диапазоне от 0 до 2π.

random.paretovariate(alpha) — распределение Парето.

random.weibullvariate(alpha, beta) — распределение Вейбулла.

Примеры¶

Генерация произвольного пароля¶

Хороший пароль должен быть произвольным и состоять минимум из 6 символов, в нём должны быть цифры, строчные и прописные буквы. Приготовить такой пароль можно по следующему рецепту:

Этот же скрипт можно записать всего в две строки:

Данная команда является краткой записью цикла for, вместо неё можно было написать так:

Данный цикл повторяется 12 раз и на каждом круге добавляет к строке psw произвольно выбранный элемент из списка.

Инициализация и состояние генератора в Python

В разделе рассмотрены функции инициализации генератора, а так же получения и установки внутреннего состояния генератора случайных чисел модуля random .

Содержание:

    random.seed() , random.getstate() , random.setstate() , random.getrandbits() .
random.seed(a=None, version=2) :

Функция random.seed() инициализирует генератор или по другому — задает его начальное состояние.

Данный метод очень удобен для воспроизводимости результатов, т.е. после инициализации одним и тем же значением a , вы будете наблюдать одни и те же случайные данные. Например если важно, что бы кто-то мог воспроизвести мои результаты на своем компьютере, то нужно обязательно указать в примере кода начальное состояние:

Если инициализировать генератор с тем же самым значением, то увидим тот же самый результат его работы:

Если аргумент a не указан или равен None , то в качестве начального состояния a используется текущее системное время. Но в случае если используется системный генератор random.SystemRandom() , то начальное состояние определяется реализацией источника энтропии самой системы.

Параметр version может быть установлен только в два значения:

  • version=1 — все биты объектов str , bytes и bytearray используются для их преобразования в объект int ;
  • version=2 — преобразует str и bytes в более узкий диапазон объектов int :

Устарело с версии Python 3.9: в будущем seed должно быть одного из следующих типов: NoneType , int , float , str , bytes или bytearray .

random.getstate() :

Функция random.getstate() возвращает кортеж с параметрами внутреннего состояния генератора, который может быть использован для воссоздания этого состояния.

Возвращаемый кортеж очень большой:

Этот кортеж может быть передан функции random.setstate() для восстановления исходного состояния генератора.

random.setstate(state) :

Функция random.setstate() задает внутреннее состояние генератора на основе кортежа с его параметрами, который можно получить с помощью функции random.getstate() .

Данная функция отличается от random.seed() тем что фиксирует состояние генератора "здесь и сейчас", т.е. состояние генератора в тот самый момент, когда оно было получено с помощью функции random.getstate() .

random.getrandbits(k) :

Функция random.getrandbits() возвращает целое число состоящее из k случайных бит.

Благодаря данному методу random.randrange() может работать со сколь угодно большими диапазонами.

Изменено в Python 3.9: теперь этот метод принимает ноль для аргумента k .

Краеугольный камень псевдослучайности: с чего начинается поиск чисел


(с)

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

Когда речь заходит о генераторах случайных (или псевдослучайных) чисел, рассказ всегда строится вокруг поиска истинной случайности. Пока серьезные математики десятилетиями ведут дискуссии о том, что считать случайностью, в практическом отношении мы давно научились использовать «правильную» энтропию. Впрочем, «шум» — это лишь вершина айсберга.

С чего начать, если мы хотим распутать клубок самых сильных алгоритмов PRNG и TRNG? На самом деле, с какими бы алгоритмами вы не имели дело, все сводится к трем китам: seed, таблица предопределенных констант и математические формулы.

Каким бы ни был seed, еще есть алгоритмы, участвующие в генераторах истинных случайных чисел, и такие алгоритмы никогда не бывают случайными.

Что такое случайность

Первое подходящее определение случайной последовательности дал в 1966 году шведский статистик Пер Мартин-Лёф, ученик одного из крупнейших математиков XX века Андрея Колмогорова. Ранее исследователи пытались определить случайную последовательность как последовательность, которая проходила все тесты на случайность.

Основная идея Мартина-Лёфа заключалась в том, чтобы использовать теорию вычислимости для формального определения понятия теста случайности. Это контрастирует с идеей случайности в вероятности; в этой теории ни один конкретный элемент пространства выборки не может быть назван случайным.

«Случайная последовательность» в представлениях Мартина-Лёфа должна быть типичной, т.е. не должна обладать индивидуальными отличительными особенностями.

Было показано, что случайность Мартина-Лёфа допускает много эквивалентных характеристик, каждая из которых удовлетворяет нашему интуитивному представлению о свойствах, которые должны иметь случайные последовательности:

  • несжимаемость;
  • прохождение статистических тестов для случайности;
  • сложность создания прогнозов.

Существование множественных определений рандомизации Мартина-Лёфа и устойчивость этих определений при разных моделях вычислений свидетельствуют о том, что случайность Мартина-Лёфа является фундаментальным свойством математики.

Seed: основа псевдослучайных алгоритмов

Первые алгоритмы формирования случайных чисел выполняли ряд основных арифметических действий: умножить, разделить, добавить, вычесть, взять средние числа и т.д. Сегодня такие мощные алгоритмы, как Fortuna и Yarrow (используется в FreeBSD, AIX, Mac OS X, NetBSD) выглядят как генераторы случайных чисел для параноиков. Fortuna, например, это криптографический генератор, в котором для защиты от дискредитации после выполнения каждого запроса на случайные данные в размере 220 байт генерируются еще 256 бит псевдослучайных данных и используются в качестве нового ключа шифрования — старый ключ при этом каждый раз уничтожается.

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

Функция rand () является простейшей из функций генерации случайных чисел в C.

В этом примере рандома используется вложенный цикл для отображения 100 случайных значений. Функция rand () хороша при создании множества случайных значений, но они являются предсказуемыми. Чтобы сделать вывод менее предсказуемым, вам нужно добавить seed в генератор случайных чисел — это делается с помощью функции srand ().

Seed — это стартовое число, точка, с которой начинается последовательность псевдослучайных чисел. Генератор псевдослучайных чисел использует единственное начальное значение, откуда и следует его псевдослучайность. Генератор истинных случайных чисел всегда имеет в начале высококачественную случайную величину, предоставленную различными источниками энтропии.

srand() принимает число и ставит его в качестве отправной точки. Если seed не выставить, то при каждом запуске программы мы будем получать одинаковые случайные числа.

Вот пример простой формулы случайного числа из «классики» — книги «Язык программирования C» Кернигана и Ричи, первое издание которой вышло аж в 1978 году:

Эта формула предполагает существование переменной, называемой random_seed, изначально заданной некоторым числом. Переменная random_seed умножается на 1 103 535 245, а затем 12 345 добавляется к результату; random_seed затем заменяется этим новым значением. Это на самом деле довольно хороший генератор псевдослучайных чисел. Если вы используете его для создания случайных чисел от 0 до 9, то первые 20 значений, которые он вернет при seed = 10, будут такими:

Если у вас есть 10 000 значений от 0 до 9, то распределение будет следующим:

0 — 10151 — 10242 — 10483 — 9964 — 9885 — 10016 — 9967 — 10068 — 9659 — 961

Любая формула псевдослучайных чисел зависит от начального значения. Если вы предоставите функции rand() seed 10 на одном компьютере, и посмотрите на поток чисел, которые она производит, то результат будет идентичен «случайной последовательности», созданной на любом другом компьютере с seed 10.

К сожалению, у генератора случайных чисел есть и другая слабость: вы всегда можете предсказать, что будет дальше, основываясь на том, что было раньше. Чтобы получить следующее число в последовательности, мы должны всегда помнить последнее внутреннее состояние генератора — так называемый state. Без state мы будем снова делать одну и ту же математическую операцию с одинаковыми числами, чтобы получить тот же ответ.

Как сделать seed уникальным для каждого случая? Самое очевидное решение — добавить в вычисления текущее системное время. Сделать это можно с помощью функции time().

Функция time() возвращает информацию о текущем времени суток, значение, которое постоянно изменяется. При этом метод typecasting гарантирует, что значение, возвращаемое функцией time(), является целым числом.

Итак, в результате добавления «случайного» системного времени функция rand() генерирует значения, которые являются более случайными, чем мы получили в первом примере.

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

В .net framework есть функция System.Security.Cryptography.RandomNumberGenerator, где в расчетах учитываются следующие факторы:

  • ID текущего процесса;
  • текущий ID потока;
  • количество отсчетов с момента загрузки;
  • текущее время;
  • различные высокоточные счетчики производительности процессора;
  • MD4-хэш среды пользователя (имя пользователя, имя компьютера и т.д.).

Но опять же, все эти числа не случайны.

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

Период (цикл) генератора

Одними из наиболее часто используемых методов генерации псевдослучайных чисел являются различные модификации линейного конгруэнтного метода, схема которого была предложена Дерриком Лемером еще в 1949 году:

Xn+1 = (aXn + c) mod m, где m — модуль, a — множитель, c — приращение, mod — операция взятия остатка от деления. Причем m > 0, 0 < a ≤ m, 0 < c ≤ m, также задается начальное значение X0: 0 < X0 ≤ m.

Линейный конгруэнтный метод дает нам повторяющиеся последовательности — конгруэнтная последовательность всегда образует «петли». Этот цикл (период), повторяющийся бесконечное число раз — свойство всех последовательностей вида Xn+1 = f(n).

В языке С линейно-конгруэнтный метод реализован в уже знакомой вам функции rand():

Что вообще такое цикл с точки зрения случайных чисел? Период — это количество чисел, которое генерируется до того, как они вернутся в той же последовательности. Для примера число периодов в шифре А5 в среднем составляет 2 23 , а сложность атаки 240, что позволяет взломать его на любом персональном компьютере.

Рассмотрим случай, когда seed равен 1, а период — 100 (на языке Haskell):

В результате мы получим следующий ответ:

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

Выбор случайного Int дает вам обратно Int и новый StdGen, который вы можете использовать для получения более псевдослучайных чисел. Многие языки программирования, включая Haskell, имеют генераторы случайных чисел, которые автоматически запоминают свое состояние (в Haskell это randomIO).

Чем больше величина периода, тем выше надежность создания хороших случайных значений, однако даже с миллиардами циклов крайне важно использовать надежный seed. Реальные генераторы случайных чисел обычно используют атмосферный шум (поставьте сюда любое физическое явление — от движения мыши пользователя до радиоактивного распада), но мы можем и схитрить программным методом, добавив в seed асинхронные потоки различного мусора, будь то длины интервалов между последними heartbeat потоками или временем ожидания mutual exclusion (а лучше добавить все вместе).

Истинная случайность бит

Итак, получив seed с примесью данных от реальных физических явлений (либо максимально усложнив жизнь будущему взломщику самым большим набором потоков программного мусора, который только сможем придумать), установив state для защиты от ошибки повтора значений и добавив криптографических алгоритмов (или сложных математических задач), мы получим некоторый набор данных, который будем считать случайной последовательностью. Что дальше?

Дальше мы возвращаемся к самому началу, к одному из фундаментальных требований — тестам.

Национальный институт стандартов и технологий США вложил в «Пакет статистических тестов для случайных и псевдослучайных генераторов чисел для криптографических приложений» 15 базовых проверок. Ими можно и ограничиться, но этот пакет вовсе не является «вершиной» проверки случайности.

Одни из самых строгих статистических тестов предложил профессор Джордж Марсалья из Университета штата Флорида. «Тесты diehard» включают 17 различных проверок, некоторые из них требуют очень длинных последовательностей: минимум 268 мегабайт.

Случайность можно проверить с помощью библиотеки TestU01, представленной Пьером Л’Экуйе и Ричардом Симардом из Монреальского университета, включающей классические тесты и некоторые оригинальные, а также посредством общедоступной библиотеки SPRNG.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *