Реализация генератора случайных чисел в 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 или С++; -)):

enter image description here

Для полноты стандартные кавычки:

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