2013-04-15 2 views
8

Я пытаюсь реализовать следующий класс:Что такое случайное распределение C++ 11?

typedef std::mt19937 Engine; 

class Interval 
{ 
public: 
    double upperBoundary; 
    double lowerBoundary; 
    double generateUniformRandomNumber(Engine& engine); 
}; 

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

Для того, чтобы генерировать случайные числа равномерно на C++ 11 образом, реализация generateUniformRandomNumber бы быть что-то вроде этого:

uniform_real_distribution<double> distribution_; // private member of Interval 

double Interval::generateUniformRandomNumber(Engine& engine) 
{ 
    return distribution_(engine); 
} 

Проблема заключается в том, что я не понимаю, C++ 11 распределения. Я знаю, что двигатели случайных чисел C++ 11 могут быть очень большими объектами (несколько килобайт), но как насчет дистрибутивов? Сначала я думал, что дистрибутивы - просто простые функторы, где operator() - это функция pure const, но, похоже, это не pure, ни const. Согласно reference, каждый экземпляр распределения имеет функцию-член reset(). Это означает, что у него есть потенциально большое внутреннее состояние или, возможно, кеш.

Мой вопрос:

  1. ли распределения имеют внутреннее состояние? Если да, то почему? Знает ли стандарт что-либо о размере этого состояния?

  2. Это хорошая идея сделать реализацию, как я? Есть ли способ лучше?

+3

В качестве примера я внедрил бета-дистрибутив (https://gist.github.com/sftrabbit/5068941), состояние которого фактически является двумя гамма-распределениями. 'reset' ничего не делает, потому что только требование состоит в том, что любые следующие значения не зависят от предыдущих применений двигателей. –

+0

@sftrabbit Итак, 'reset()' там, если кто-то создает дистрибутив, который накапливает состояние при вызове 'operator()'? Например, каждый последующий вызов 'operator()' может дать вам возрастающий диапазон ... что также объясняет, почему 'operator()' не const, я думаю? – David

+0

@Dave Да, нет причин, по которым распространение не может измениться, поскольку вы продолжаете вызывать 'operator()'. Это означает, что результирующие значения зависят от предыдущих вызовов. –

ответ

2

Посмотрите на документы для RandomNumberDistribution template policy ...

reset():

Сбрасывает внутреннее состояние объекта распределения. После вызова этой функции следующий вызов оператора() объекта распределения не будет зависеть от предыдущих вызовов оператора().

Это означает, что вызовы operator() могут изменить состояние, которое вызывает последующие вызовы operator(). Вот почему reset() существует и почему operator() не const.

uniform_real_distribution должен быть небольшим простым функтором, как вы сказали. Скорее всего, это будет просто 2 Real s, с которой он был построен, и ничего больше. И reset() ничего не должен делать для этого.

6

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

  1. Для распределения случайного числа очень часто есть некоторые параметры для его настройки. Например, нормальное распределение имеет среднее значение и параметры дисперсии. Это часть его состояния, потому что они должны поддерживаться между вызовами.

  2. Распространение реализовано в терминах другого распространения. Вы можете просто подумать об этом как о более сложном параметре. Например, моя реализация beta distribution сохраняет два гамма-распределения, каждый из которых имеет свою собственную конфигурацию.

  3. Распределение может меняться со временем. Нечего сказать, что повторные вызовы распределения должны быть независимыми. Здесь входит функция члена reset. Большинство дистрибутивов имеют независимые вызовы operator(), поэтому функция reset фактически ничего не делает (ее определение пуст). Однако, если ваши вызовы зависят, reset должен вернуть дистрибутив в состояние, в котором следующий вызов является независимым.

Ваше воплощение кажется прекрасным. A uniform_real_distribution<double> очень маловероятно, чтобы у вас было гораздо больше состояний, с которыми вы строите параметры.

2

Да, распределения могут иметь внутренние состояния. Они, как правило, небольшие, но если вас беспокоит размер, проверьте его.

2

Спецификация для reset() состояния:

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

Это означает, что обычно вызовы operator() может зависеть от значений, полученных с помощью двигателей, используемых в предшествующем уровне вызова operator(). То есть, дистрибутив может кэшировать или иным образом зависеть от результатов предыдущих движков.

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

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

Так что да, дистрибутивы могут иметь внутреннее состояние. Стандарт не устанавливает требования к размеру этого состояния.

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

Смежные вопросы