Проблемы с повторной загрузкой генератора псевдослучайных чисел?

StackOverflow https://stackoverflow.com/questions/976993

Вопрос

Я видел немало рекомендаций о том, как не заполнять генераторы псевдослучайных чисел более одного раза за выполнение, но никогда не сопровождался подробным объяснением.Конечно, легко понять, почему следующий пример (C/C++) не является хорошей идеей:

int get_rand() {
  srand(time(NULL));
  return rand();
}

с момента звонка get_rand несколько раз в секунду дает повторяющиеся результаты.

Но не будет ли следующий пример приемлемым решением?

MyRand.h

#ifndef MY_RAND_H
#define MY_RAND_H

class MyRand
{
  public:
    MyRand();
    int get_rand() const;
  private:
    static unsigned int seed_base;
};

#endif

MyRand.cpp

#include <ctime>
#include <cstdlib>
#include "MyRand.h"

unsigned int MyRand::seed_base = static_cast<unsigned int>(time(NULL));

MyRand::MyRand()
{
  srand(seed_base++);
}

int MyRand::get_rand() const
{
  return rand();
}

main.cpp

#include <iostream>
#include "MyRand.h"

int main(int argc, char *argv[]) 
{
  for (int i = 0; i < 100; i++) 
  {
    MyRand r;
    std::cout << r.get_rand() << " ";
  }
}

то естьНесмотря на то MyRandКонструктор :s вызывается несколько раз подряд, каждый вызов srand имеет другой параметр.Очевидно, что это не является потокобезопасным, но, опять же, это тоже не является потокобезопасным. rand.

Это было полезно?

Решение

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

Когда вы запускаете генератор случайных чисел, вы по сути устанавливаете это внутреннее состояние.Если вы сбросите внутреннее состояние до какого-то предсказуемого значения, вы потеряете видимость случайности.

Например, популярный простой ГСЧ представляет собой линейный конгруэнтный генератор.Числа генерируются следующим образом:

X[n+1] = (a X[n] + c) mod m

В этом случае X[n+1] — это и результат, и новое внутреннее состояние.Если вы каждый раз заполняете генератор, как вы предлагаете выше, вы получите последовательность, которая выглядит следующим образом:

{(ab + c) mod m, (a(b+1) + c) mod m, (a(b+2) + c) mod m, ...}

где б твой seed_base.Это вовсе не выглядит случайным.

Другие советы

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

Это действительно зависит от того, почему вы хотите генерировать случайные числа и насколько «случайный» является для вас приемлемым случайным числом.В вашем примере он может избегать быстрых последовательных дубликатов, и этого может быть достаточно для вас.В конце концов, важно то, что он работает.

Почти на каждой платформе есть лучший способ генерации случайных чисел, чем rand().

Ну, это дополнительная обработка, которую делать не нужно.

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

Я бы не подумал, что ваш метод какой-либо более случайный, чем это.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top