Проблемы с заполнением генератора псевдослучайных чисел более одного раза? - PullRequest
5 голосов
/ 10 июня 2009

Я видел довольно много рекомендаций, чтобы не заполнять генераторы псевдослучайных чисел более одного раза за исполнение, но никогда не сопровождалось подробным объяснением. Конечно, легко понять, почему следующий пример (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.

Ответы [ 4 ]

6 голосов
/ 10 июня 2009

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

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

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

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, ...}

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

1 голос
/ 10 июня 2009

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

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

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

1 голос
/ 10 июня 2009

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

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

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

0 голосов
/ 10 июня 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...