У меня есть класс, которому нужны случайные числа в паре разных методов-членов. я использую C++11
и я думаю, что невозможно создать новый генератор случайных чисел в каждом методе.
Можно ли разделить генератор случайных чисел по всему классу, сделав его атрибутом члена класса или, возможно, typedef, при этом обеспечивая хороший хаотичность?
Как бы я это сделал, может быть, вы можете указать мне маленький пример? Где я должен установить семя для случайного двигателя, я хочу использовать Mersenne twister
двигатель с разными типами распределений (normal
& uniform
).
Движки и распределения являются значениями и могут быть членами, как и другие объекты с типами значений.
Вы должны запустить двигатель, когда он будет создан, что означает, если он является членом, когда ваш объект создан. Мой пример использует инициализатор в классе с random_device
посеять двигатель по умолчанию. Это также позволяет указать семена для воспроизводимых, проверяемых результатов.
Я бы не стал раскрывать слишком много деталей реализации, таких как предоставление более полного интерфейса для взаимодействия с движком, поскольку это нарушает инкапсуляцию. Это должны быть внутренние, скрытые детали реализации.
std::mt19937 make_seeded_engine() {
std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
return std::mt19937(seed);
}
struct Foo {
std::mt19937 eng = make_seeded_engine();
std::uniform_int_distribution<> dist1 {1, 20};
std::uniform_real_distribution<> dist2 {0.0, 100.0};
Foo() = default;
template<typename SeedSeq>
Foo(SeedSeq &&seed) : eng(seed) {}
int bar() {
return dist1(eng);
}
double baz() {
return dist2(eng);
}
};
Вы должны по крайней мере хранить механизм между использованиями, потому что гарантии равномерного распределения случайной последовательности действуют только для последовательностей повторных вызовов одного и того же объекта механизма. Там нет никакой гарантии о последовательности, созданной с использованием различных двигателей.
И на самом деле то же самое относится и к дистрибутивам, хотя я не знаю реализаций, где создание нового дистрибутива каждый раз приводило бы к неверным результатам (однако есть реализации, где распределения кэшируют значения по разным причинам, поэтому создание дистрибутивов каждый раз может работать хуже и производить другую последовательность).
Например, общий алгоритм вычисления нормального распределения выдает два значения одновременно. Реализации `std :: normal_distribution делают это и кэшируют второе значение, чтобы использовать любой другой вызов. Следующая программа демонстрирует это.
#include <iostream>
#include <random>
int main() {
typedef std::mt19937 Engine;
typedef std::normal_distribution<> Distribution;
Engine eng(1);
Distribution dist;
for (int i=0; i<10; ++i)
std::cout << dist(eng) << ' ';
std::cout << '\n';
eng.seed(1);
for (int i=0; i<10; ++i)
std::cout << Distribution()(eng) << ' ';
std::cout << '\n';
}
С VC ++ 2012 я получаю вывод:
0.156066 0.3064 -0.56804 -0.424386 -0.806289 -0.204547 -1.20004 -0.428738 -1.18775 1.30547
0.156066 -0.56804 -0.806289 -1.20004 -1.18775 -0.153466 0.133857 -0.753186 1.9671 -1.39981
Обратите внимание, что последовательность, создаваемая при создании нового распределения на каждой итерации, содержит только все остальные значения последовательности, созданной с помощью одного распределения.
Можно ли разделить генератор случайных чисел по всему классу, сделав его атрибутом члена класса или, возможно, typedef, при этом обеспечивая при этом хорошую случайность?
Абсолютно. При условии, что вы инициализируете его с переменным начальным числом (или позволите ему использовать значение по умолчанию), вы должны получать «хорошую» случайность при каждом вызове ГСЧ. На самом деле, я бы предположил, что использование отдельного ГСЧ для каждого метода не только дорого, но и плохой дизайн.
Что касается того, как реализовать различные дистрибутивы, http://en.cppreference.com/w/cpp/numeric/random есть несколько хороших примеров (таких как этот).