Как правильно инициализировать С++ 11 std:: seed_seq

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

Я вижу, что существует класс std::seed_seq, который, как представляется, предназначен для этой цели, но неясно, как правильно его построить. Примеры, которые я видел, например на странице cppreference.com, инициализируют его с помощью нескольких целых констант, жестко закодированных в программе:

std::seed_seq seq{1,2,3,4,5};

Я сомневаюсь, что на самом деле рекомендуемая передовая практика, поэтому мне интересно, что такое рекомендуемая практика. В частности:

  • Так как a seed_seq можно инициализировать с произвольным числом целых чисел, какое значение длины его списка инициализаторов? Если я хочу производить семена для 100 случайных генераторов, нужно ли инициализировать мой seed_seq с 100 целыми числами?
  • Если длина списка инициализаторов не должна соответствовать количеству семян, которые я намереваюсь сгенерировать, нормально ли инициализировать seed_seq только одним целым числом, а затем использовать его для создания большого количества семян?
  • Как насчет инициализации без целых чисел, т.е. с использованием конструктора по умолчанию? (Это означает, что каждый раз, конечно, я получаю одинаковые семена).
  • Если это ОК, чтобы построить seed_seq из одного целого числа, а затем генерировать много семян от него, что пользы от использования seed_seq вместо обычного случайного генератора? Почему бы просто не построить a std::mt19937 из этого единственного целого и использовать его для создания начальных значений для других генераторов?

Ответ 1

Проблема с использованием фиксированной последовательности - это то, что вы получаете из нее одну и ту же последовательность семян, почти так же, как если бы вы написали srand(42) в начале вашей программы: она генерирует идентичные последовательности.

Стандартные состояния С++ 11 (в разделе 26.5.7.1 Class seed_seq):

Серийная последовательность представляет собой объект, который потребляет последовательность целочисленных данных и выдает запрошенное количество целых значений без знака i, 0 я < 2 32 на основе потребляемых данных.

[Примечание: такой объект обеспечивает механизм, позволяющий избежать репликации потоков случайных вариаций. Это может быть полезно, например, в приложениях, требующих большого количества двигателей с произвольным числом. -end note]

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

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

То, что я обычно делаю, очень похоже на то, как вы обычно рандомизируете один генератор, с srand (time (0)). Другими словами:

#include <random>
#include <cstdint>
#include <ctime>
#include <iostream>

int main()
{
    std::seed_seq seq{time(0)};
    std::vector<std::uint32_t> seeds(10);
    seq.generate(seeds.begin(), seeds.end());
    for (std::uint32_t n : seeds) {
        std::cout << n << '\n';
    }
}

Если у вас есть несколько источников случайности, таких как значение, считанное с /dev/random под Linux, или генератор белого шума некоторого описания или среднее число миллисекунд между нажатиями клавиш при последнем запуске этой программы пользователем, вы могут использовать их как дополнительные входы:

std::seed_seq seq{time(0), valFromDevRandom(), getWhiteNoise(), avgMillis()};

но я сомневаюсь, что константы - это путь, так как они не добавляют случайности к уравнению.