В статье приведен класс-синглтон для запуска генератора псевдослучайных чисел.
Внимание! Предложенный класс является не потокобезопасным!
В C++11 появилось много классов для работы по генерации случайных чисел, качество которых куда лучше стандартного rand().
Для генерации будем использовать генератор std::mt19937 вихря Мерсенна. Минус его заключается в том, что желательно во всей программе использовать один экземпляр данного генератора, что куда менее удобно, чем было с стандартным генератором rand() (один раз запустили strand() и всё).
Поэтому предлагается использовать класс-синглтон, который будет возвращать ссылку на экземпляр класса std::mt19937. При таком подходе в одном запуске программы будет использоваться только один экземпляр.
Как и любой генератор псевдо-случайных чисел, наш генератор нужно инициализировать. В C++11 появился очень крутой тип std::random_device. Он генерирует истинно случайное число благодаря устройствам на компьютере (например, через источник энтропии на Intel чипах: я не смог найти информацию о природе этого источника). Но не на всех устройствах есть возможность генерирования таких случайных чисел. А некоторые компиляторы просто не обращаются к этому устройству (например, MinGW).
Поэтому в классе предусмотрен вариант, что если не получилось использовать данный генератор, то будемиспольовать стандартную инициализацию временем.
Сам класс.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class RandomGenerator { public: static std::mt19937 & getMt19937(); private: RandomGenerator(); ~RandomGenerator() {} static RandomGenerator& instance(); RandomGenerator(RandomGenerator const&) = delete; RandomGenerator& operator= (RandomGenerator const&) = delete; std::mt19937 mMt; }; RandomGenerator::RandomGenerator() { std::random_device rd; if (rd.entropy() != 0) { std::seed_seq seed{rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()}; mMt.seed(seed); } else { auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); mMt.seed(seed); } } RandomGenerator& RandomGenerator::instance() { static RandomGenerator s; return s; } std::mt19937 & RandomGenerator::getMt19937() { return RandomGenerator::instance().mMt; } |
Пример использования.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
#include <iostream> #include <random> #include <chrono> class RandomGenerator { public: static std::mt19937 & getMt19937(); private: RandomGenerator(); ~RandomGenerator() {} static RandomGenerator& instance(); RandomGenerator(RandomGenerator const&) = delete; RandomGenerator& operator= (RandomGenerator const&) = delete; std::mt19937 mMt; }; RandomGenerator::RandomGenerator() { std::random_device rd; if (rd.entropy() != 0) { std::seed_seq seed{ rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd() }; mMt.seed(seed); } else { auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); mMt.seed(seed); } } RandomGenerator& RandomGenerator::instance() { static RandomGenerator s; return s; } std::mt19937 & RandomGenerator::getMt19937() { return RandomGenerator::instance().mMt; } int main() { std::mt19937 &mt = RandomGenerator::getMt19937(); std::uniform_real_distribution<double> dist(0.0, 1.0); for (std::size_t i = 0; i < 5; i++) std::cout << dist(mt) << "\n"; std::cout << "\n"; std::mt19937 &mt2 = RandomGenerator::getMt19937(); std::uniform_real_distribution<double> dist2(0.0, 1.0); for (std::size_t i = 0; i < 5; i++) std::cout << dist2(mt2) << "\n"; return 0; } |
Пример вывода.
Класс проверен на компиляторах: Visual Studio 2015, MinGW, DevC++, GCC.