Harrix Блог

  • Списки статей
    • Все статьи
    • IT
    • Qt
    • C++
    • Сложение двух чисел
    • Web программированиe
    • FAQ
    • Latex
    • Установка программ
    • Мифы
    • Видео
    • Про фото
  • Проекты
  • Harrix.org
  • RSS
  • Контакты

Класс генератора случайных чисел для C++11

Класс-синглтон.

11.05.2017 3 комментария 6 673 просмотров

В статье приведен класс-синглтон для запуска генератора псевдослучайных чисел.

Внимание! Предложенный класс является не потокобезопасным!

В 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.


Статьи по теме:

  1. Как сохранить данные из QTextEdit в ODF
  2. Сложение двух чисел в Visual Studio 2013 на С++ (CLR приложение)
  3. Сложение двух чисел в C++ Builder 6 на C++ (консольное приложение)
  4. Считывание табличных данных из файла на C++

IT C++, C++11, Случайные числа

© 2014 Harrix