Генератор случайных чисел: должен ли он использоваться в качестве одиночного?

Я использую случайные числа в нескольких местах и ​​обычно создаю генератор случайных чисел, когда мне это нужно. В настоящее время я использую алгоритм Marsaglia Xorshift для заполнения его текущим системным временем.
Теперь у меня есть некоторые сомнения по поводу этой стратегии:
Если я использую несколько генераторов, независимость (случайность) чисел между генераторами зависит от начального числа (одно и то же начальное число от одного и того же числа). Поскольку я использую время (нс) в качестве начального числа и с тех пор, как это время меняется, это работает, но мне интересно, не лучше ли было бы использовать только один генератор единственного числа и, например, сделать его доступным как синглтон. Повысит ли это качество случайных чисел?

Изменить: К сожалению, C ++ 11 пока не вариант

Изменить: Чтобы быть более конкретным: я не предполагаю, что синглтон может повысить качество случайных чисел, но тот факт, что используется только один генератор и сеяны. В противном случае я должен быть уверен, что семена разных генераторов независимы (случайны) от другого.
Крайний пример: я сею два генератора с одинаковым номером -> между ними нет случайности

4

Решение

Предположим, у вас есть несколько переменных, каждая из которых должна быть случайной, независимой от других, и будет регулярно переназначаться новым случайным значением из некоторого генератора случайных чисел. Это часто случается с анализом Монте-Карло и играми (хотя строгость к играм гораздо меньше, чем к Монте-Карло). Если бы существовал идеальный генератор случайных чисел, было бы хорошо использовать один его экземпляр. Назначьте Nго псевдослучайное число от генератора к переменной Икс1, следующее случайное число в переменной Икс2, рядом с Икс3, и так далее, в конечном итоге возвращаясь к переменной Икс1 на следующем цикле. вокруг. Здесь есть проблема: слишком много PRNG не проходят тест на независимость и не проходят тест на независимость при использовании таким способом, а некоторые даже не проходят тесты на случайность отдельных последовательностей.

Мой подход заключается в использовании одного генератора PRNG в качестве генератора семян для набора N примеры автономных PRNG. Каждый экземпляр этих последних PRNG содержит одну переменную. Под автономностью я подразумеваю, что PRNG — это объект, состояние которого поддерживается в элементах экземпляра, а не в статических элементах или глобальных переменных. Генератор семян даже не должен быть из той же семьи, что и другие N PRNGs. Он просто должен быть реентерабельным в случае, когда несколько потоков одновременно пытаются использовать генератор семян. Тем не менее, в моем использовании я считаю, что лучше всего настроить PRNG перед началом многопоточности, чтобы гарантировать повторяемость. Это один прогон, одно исполнение. Методы Монте-Карло, как правило, требуют тысячи казней, может быть, больше, может быть, намного больше. С Монте-Карло повторяемость очень важна. Так что нужен еще один генератор случайных семян. Это семя генератор семян, используемый для генерации N генераторы для переменных.

Повторяемость важна, по крайней мере, в мире Монте-Карло. Предположим, что прогон № 10234 длинного моделирования Монте-Карло приводит к серьезному провалу. Было бы приятно увидеть, что в мире произошло. Это могло быть статистической случайностью, это могло быть проблемой. Проблема заключается в том, что в типичной настройке MC записывается только минимум данных, достаточный для вычисления статистики. Чтобы увидеть, что произошло в прогоне № 10234, нужно повторить этот конкретный случай, но теперь запишите все.

2

Другие решения

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

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

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

1

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

0

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

Это хороший пример, когда синглтон не является анти-паттерном. Вы также можете использовать какой-то инверсия контроля.

Повысит ли это качество случайных чисел?

Нет. Качество зависит от алгоритма, который генерирует случайные числа. Как вы используете это не имеет значения (при условии, что он используется правильно).

Для вашего редактирования: вы можете создать какой-то контейнер, содержащий объекты ваших классов RNG (или использовать существующие контейнеры). Что-то вроде этого :

std::vector< Rng > & RngSingleton()
{
static std::vector< Rng > allRngs( 2 );
return allRngs;
}

struct Rng
{
void SetSeed( const int seen );
int GenerateNumber() const;
//...
};

// ...
RngSingleton().at(0).SetSeed( 55 );
RngSingleton().at(1).SetSeed( 55 );
//...
const auto value1 = RngSingleton().at(0).GenerateNumber;
const auto value2 = RngSingleton().at(1).GenerateNumber;
0

Заводской образец на помощь.
Клиент никогда не должен беспокоиться о правилах создания экземпляров своих зависимостей.
Это позволяет менять методы создания. И наоборот, если вы решите использовать другой алгоритм, вы можете поменять класс генератора, и клиенты не нуждаются в рефакторинге.
http://www.oodesign.com/factory-pattern.html

—РЕДАКТИРОВАТЬ

Добавлен псевдокод (извините, это не c ++, это слишком долго, так как я последний раз работал в нем)

interface PRNG{
function generateRandomNumber():Number;
}

interface Seeder{
function getSeed() : Number;
}

interface PRNGFactory{
function createPRNG():PRNG;
}

class MarsagliaPRNG implements PRNG{
constructor( seed : Number ){
//store seed
}

function generateRandomNumber() : Number{
//do your magic
}
}

class SingletonMarsagliaPRNGFactory implements PRNGFactory{
var seeder : Seeder;
static var prng : PRNG;
function createPRNG() : PRNG{
return prng ||= new MarsagliaPRNG( seeder.getSeed() );
}
}

class TimeSeeder implements Seeder{
function getSeed():Number{
return now();
}
}

//usage:
seeder : Seeder = new TimeSeeder();
prngFactory : PRNGFactory = new SingletonMarsagliaPRNGFactory();

clientA.prng = prngFactory.createPRNG();
clientB.prng = prngFactory.createPRNG();
//both clients got the same instance.

Большим преимуществом является то, что если вы хотите / должны изменить какие-либо детали реализации, в клиентах ничего не должно меняться. Вы можете изменить метод заполнения, алгоритм RNG и правило создания экземпляров, не касаясь любого клиента в любом месте.

-1
По вопросам рекламы [email protected]