Реализация генератора случайных чисел в C/С++
Меня немного смущает реализация генератора случайных чисел в C, который также явно отличается от реализации в С++
Если я правильно понял, вызов "srand (seed)" каким-то образом инициализирует скрытую переменную (семя), доступную с помощью "rand()", которая, в свою очередь, указывает функцию на предварительно сгенерированную последовательность, например для пример этого. Каждый последующий вызов "rand()" продвигает последовательность (и, видимо, существуют другие способы продвижения на С++), что также предполагает использование внутреннего скрытого указателя или счетчика для отслеживания продвижения.
Я нашел много дискуссий о том, как работают алгоритмы для генерации псевдослучайных чисел и документации по функциям rand() и srand(), но не смогли найти информацию об этих скрытых параметрах и их поведении, кроме того, что в соответствии с этим источником, они не являются потокобезопасными.
-
Может ли кто-нибудь здесь пролить свет на то, как определяются эти параметры и каково должно быть их определенное поведение в соответствии со стандартами, или если их поведение определяется реализацией?
-
Ожидают ли они быть локальными для функции/метода, который вызывает rand() и srand()? Если да, существует ли способ передать их другой функции/методу?
Если ваш ответ специфичен для C или С++, пожалуйста, будьте любезны, чтобы указать на это. Любая информация будет высоко оценена. Пожалуйста, имейте в виду, что этот вопрос касается не предсказуемости данных, генерируемых rand() и srand(), а о требованиях, статусе и функционировании их внутренних переменных а также их доступности и охвата.
Ответы
Ответ 1
Требованиями rand
являются:
- Генерирует псевдослучайные числа.
- Диапазон от 0 до
RAND_MAX
(минимум 32767).
- Размер семени, заданный
srand()
, определяет последовательность возвращаемых псевдослучайных чисел.
- Он не должен быть потокобезопасным или даже реентерабельным, состояние может храниться в статической переменной.
Стандарт не определяет способ восстановления внутреннего состояния для повторного использования или чего-либо еще.
Нет требований к реализации PRNG, поэтому каждая реализация может иметь свои собственные, хотя Линейные конгруэнтные генераторы являются фаворитами.
Соответствующая (хотя и бесполезная) реализация представлена в этой полосе разведения:
http://dilbert.com/strips/comic/2001-10-25/
Или для тех, кто любит XKCD (это идеальное дополнение для любой библиотеки C или С++; -)):
Для полноты стандартные кавычки:
7.22.2.1 Функция rand
Функция rand вычисляет последовательность псевдослучайных целых чисел в диапазоне от 0 до RAND_MAX.
Функция rand не требуется, чтобы избежать гонок данных с другими вызовами псевдослучайных функции генерации последовательности. Реализация должна вести себя так, как если бы никакая функция библиотеки вызывает функцию rand.
[...]
Значение макроса RAND_MAX должно быть не менее 32767.
7.22.2.2 Функция srand
Функция srand использует аргумент как семя для новой последовательности псевдослучайных числа, которые будут возвращены последующими вызовами rand. Если srand вызывается с помощью одно и то же значение семени, последовательность псевдослучайных чисел должна повторяться. Если randвызываемый до того, как были сделаны какие-либо вызовы srand, должна быть сгенерирована одна и та же последовательность как когда srand сначала вызывается с начальным значением 1.
Функция srand не требуется, чтобы избежать гонок данных с другими вызовами для псевдослучайных функции генерации последовательности. Реализация должна вести себя так, как если бы никакая функция библиотеки вызывает функцию srand.
С++ включает в себя rand
, srand
и RAND_MAX
без изменений по ссылке из стандарта C.
Существует несколько функций/классов библиотеки С++, которые явно документированы для использования генератора случайных чисел C.
Ответ 2
Следующий ответ для C; в частности, стандарт 1999 года.
Стандарт C99 очень легк в реальных деталях реализации для rand
и srand
. Он просто утверждает, что аргумент srand
используется "как seed для новой последовательности псевдослучайных чисел, которые должны быть возвращены посредством последующие вызовы rand
."
На практике, как правило, он работает:
- Библиотека C определяет целочисленную переменную, которая используется
rand
и srand
для отслеживания состояния PRNG.
-
srand
устанавливает переменную состояния в заданное значение.
-
rand
принимает значение переменной состояния и выполняет некоторую математическую магию для создания двух новых целых чисел: одно - это псевдослучайное число, которое оно возвращает, а другое становится новым значением для переменной состояния, таким образом влияя на следующий вызов rand
(предполагая, что srand
не вызывается до этого).
В стандарте C приведен пример возможной реализации rand
и srand
, которая проявляет это поведение:
static unsigned long int next = 1;
int rand(void) // RAND_MAX assumed to be 32767
{
next = next * 1103515245 + 12345;
return (unsigned int)(next/65536) % 32768;
}
void srand(unsigned int seed)
{
next = seed;
}