2017-01-25 9 views
0

Я выполняю симуляцию 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- Почему разные семена получают одинаковые результаты.

+0

Мне не приходилось иметь дело с этим раньше, поэтому я не уверен на это на 100%, но я думал, что случайные библиотеки STL не являются потокобезопасными с точки зрения вызова одновременно. (Вы можете использовать один генератор/дистрибутив в нескольких потоках, если вы позаботитесь о синхронизации, которую я считаю). Кроме того, еще одна проблема с генераторами случайных чисел для посева в многопоточной среде заключается в том, что если вы используете семена на основе времени, многие функции времени имеют разрешение на курс, так что несколько потоков, вызывающих одну и ту же вещь примерно в одно и то же время, могут получить такой же результат. – RyanP

ответ

0

Поддержка случайных чисел в STL не является потокобезопасной. Вы можете добавить это (за счет некоторой производительности во время выполнения) или перейти с отдельными генераторами случайных чисел для каждого потока. Каждому генератору потока потребуется отдельное семя. В простейшей форме это может быть достигнуто путем увеличения начального значения для каждого нового потока, но при этом выполняется риск дублирования последовательностей, если новая симуляция запускается слишком быстро. Вы также можете масштабировать свое существующее начальное значение с помощью соответствующей константы, а затем добавить к нему индекс потока. Или вы можете посмотреть в seed_sequence s и использовать индекс потока или идентификатор в качестве вторичного ввода в последовательность.

+0

Я использую семена с шагом 1. Каждый поток вызывается почти сразу после другого. Если семя является пользовательским вводом и не зависит от времени, почему вы говорите, что «запускает риск дублирования последовательностей, если новая симуляция запускается слишком быстро»? – g1368

+0

@ g1368 Это относится к семенам, основанным на времени. – 1201ProgramAlarm

+0

@ g1368 Каждый поток нуждается в собственной копии '_unif' (и любых других данных, которые записываются во время моделирования). – 1201ProgramAlarm