Я выполняю симуляцию monte carlo на нескольких потоках. У меня есть класс Draw, который обрабатывает генерацию случайных чисел. Он использует mt19937-64 в качестве генератора std::uniform_real_distribution
.Создание случайных чисел с mt19937_64 для многопоточного моделирования monte carlo
template<typename T, typename R>
class Draw {
T _dist;
typedef decltype(_dist.min()) result_type;
public:
// Draw : T R -> Draw
//! GIVEN
//! 1. dist - A distribution.
//! 2. gen - A random number generator.
Draw(T dist, R gen) : _dist(dist), _gen(gen) {}
virtual ~Draw() = default;
//() : -> Value
//! GIVEN
//! RETURNS
//! value drawn from the distribution, which can be any result type
//! supported by the distribution.
result_type operator()() const { return _draw(); }
// seed : NonNegInt -> Void
//! GIVEN
//! 1. seed - A random number generator (RNG) seed.
//! EFFECT
//! Seeds the RNG with the given seed.
void seed(unsigned long seed) { _gen.seed(seed);}
private:
R _gen;
// draw : -> Value
// GIVEN:
// RETURNS: A value drawn from the distribution, which can be any result
// type supported by the distribution.
std::function<result_type()> _draw = bind(_dist,_gen);
};
// standard uniform distribution ~ Unif(a=0, b=1)
class DrawUnif :
public Draw<std::uniform_real_distribution<double>,std::mt19937_64>
{
typedef std::mt19937_64 genny;
typedef std::chrono::system_clock clk;
typedef std::uniform_real_distribution<double> dist;
public:
DrawUnif() :
Draw(dist{0,1}, genny(clk::now().time_since_epoch().count())) {}
//! GIVEN
//! -----
//! 1. seed - A seed for the random number generator.
DrawUnif(unsigned long seed) : Draw(dist{0,1}, genny(seed)) {}
virtual ~DrawUnif() = default;
};
Каждый поток имеет доступ к следующему общему указателю
typedef std::shared_ptr<DrawUnif> DrawUnifShrPtr;
DrawUnifShrPtr _unif;
которая инициализируется
_unif(DrawUnifShrPtr(new DrawUnif { seed }));
Каждого поток имеет несколько функций, которые часто называют draw(*_unif)
для генерации случайного числа. Результаты кажутся правильными, но мне интересно, есть ли в двух потоках
draw(*_unif)
в то же время, что произойдет?
Затем я выделил новый shared_pointer с различными семенами в каждой группе:
_unif(std::make_shared<DrawUnif>(*c._unif));
_unif->seed(a_new_seed);
Теперь результаты выглядят wiered! Каждая нить становится точно такой же случайной !! чисел и обеспечить тот же результат. Подводя итог:
1 - Что произойдет, если несколько ступеней вызовут ничью одновременно?
2- Почему разные семена получают одинаковые результаты.
Мне не приходилось иметь дело с этим раньше, поэтому я не уверен на это на 100%, но я думал, что случайные библиотеки STL не являются потокобезопасными с точки зрения вызова одновременно. (Вы можете использовать один генератор/дистрибутив в нескольких потоках, если вы позаботитесь о синхронизации, которую я считаю). Кроме того, еще одна проблема с генераторами случайных чисел для посева в многопоточной среде заключается в том, что если вы используете семена на основе времени, многие функции времени имеют разрешение на курс, так что несколько потоков, вызывающих одну и ту же вещь примерно в одно и то же время, могут получить такой же результат. – RyanP