В своих прошлых статьях по изучению C ++ я использовал старые методы C-стиля для генерации случайных чисел. В этой статье я собираюсь представить более современный способ (начиная с C ++ 11) генерировать случайные числа на C ++ с использованием класса random.
Генерация случайных чисел в стиле C
В своих предыдущих статьях по изучению стандартной библиотеки шаблонов C ++ (STL) я использовал старый метод C-стиля генерации случайных чисел. Вот программа, демонстрирующая эту технику:
#include <iostream> #include <vector> #include <cstdlib> #include <ctime> using namespace std; int main () { vector<int> numbers; srand(time(0)); for (int i = 1; i <= 10; i++) { numbers.push_back(rand() % 100 + 1); } for (const int n : numbers) { cout << n << " "; } return 0; }
Результат одного запуска этой программы:
29 8 36 98 93 27 56 91 97 4
Функция rand
генерирует случайное число и находится в cstdlib
. Функция srand
обеспечивает начальное значение для функции rand
, чтобы последовательность случайных чисел не повторялась.
Не вдаваясь в подробности (но некоторые из них вы можете увидеть здесь), функция rand
менее чем идеальна для генерации случайных чисел. Стандартная библиотека C ++ теперь имеет лучший набор функций для генерации случайных чисел в классе random
. В оставшейся части этой статьи мы кратко рассмотрим, как использовать этот класс.
Случайный класс - движки и распределения
Класс random
имеет набор механизмов случайных чисел, которые являются источниками случайных значений, а также содержит набор распределений случайных чисел, которые превращают значения механизма в случайные числа.
В стандартной библиотеке C ++ есть 16 механизмов случайных чисел, поэтому я не могу охватить их все в этой статье. Вероятно, вы будете чаще всего использовать default_random_engine
, который является зависящим от реализации механизмом для создания случайных значений. Два других двигателя - linear_congruential_engine
и mersenne_twister_engine
. Пожалуйста, обратитесь к документации стандартной библиотеки C ++ для получения дополнительной информации об этих движках.
В стандартной библиотеке около двадцати дистрибутивов, преобразующих случайные значения в случайные числа. Два, которые вы, вероятно, будете использовать чаще всего, и те, которые я собираюсь продемонстрировать в этой статье, это uniform_int_distribution
и uniform_real_distribution
. Общие группы распределений - это распределения Бернулли, распределения Пуассона, нормальные распределения и распределения выборки. Как и в случае с движками, пожалуйста, обратитесь к документации стандартной библиотеки для получения дополнительной информации об этих дистрибутивах.
Использование случайного класса
Теперь давайте посмотрим, как мы можем использовать класс random
для генерации случайных чисел, необходимых для наших приложений C ++. В этом первом примере используются default_random_engine
и uniform_int_distribution
для заполнения целочисленного вектора:
#include <iostream> #include <vector> #include <random> using namespace std; int main () { vector<int> numbers; default_random_engine defEngine; uniform_int_distribution<int> intDistro(0,100); for (int i = 1; i <= 20; i++) { numbers.push_back(intDistro(defEngine)); } for (const int n : numbers) { cout << n << " "; } return 0; }
Вот результат одного запуска этой программы:
0 13 76 46 53 22 4 68 68 94 38 52 83 3 5 53 67 0 38 6
Вы должны отметить здесь пару вещей. Во-первых, когда вы объявляете распределение, вы также указываете диапазон чисел, который должен использовать дистрибутив, как в этой строке кода:
uniform_int_distribution<int> intDistro(0,100);
Затем вы генерируете случайное число, вызывая объект распределения с объектом движка в качестве аргумента, как в этой строке:
numbers.push_back(intDistro(defEngine));
Мы можем увидеть, насколько хорошо работает генератор случайных чисел, очистив вектор и сгенерировав новый набор чисел, как в этом примере:
int main () { vector<int> numbers; default_random_engine defEngine; uniform_int_distribution<int> intDistro(0,100); for (int i = 1; i <= 20; i++) { numbers.push_back(intDistro(defEngine)); } for (const int n : numbers) { cout << n << " "; } numbers.clear(); for (int i = 1; i <= 20; i++) { numbers.push_back(intDistro(defEngine)); } cout << endl << endl; for (const int n : numbers) { cout << n << " "; } return 0; }
Результат одного запуска этой программы:
0 13 76 46 53 22 4 68 68 94 38 52 83 3 5 53 67 0 38 6 42 69 59 93 85 53 9 66 42 70 91 76 26 4 74 33 63 76 100 36
Вы заметите, что первый набор чисел при втором запуске программы такой же, как и при первом запуске. Это означает, что нам нужно предоставить начальное число, чтобы не повторять шаблоны чисел.
Решение этой проблемы - предоставить начальное число для двигателя при его объявлении. Следующая программа предоставляет такое начальное число, используя функцию time
из файла заголовка ctime
для возврата системного времени. Вот код:
#include <vector> #include <random> #include <ctime> using namespace std; int main () { vector<int> numbers; default_random_engine defEngine(time(0)); uniform_int_distribution<int> intDistro(0,100); for (int i = 1; i <= 20; i++) { numbers.push_back(intDistro(defEngine)); } for (const int n : numbers) { cout << n << " "; } return 0; }
Теперь вы будете получать разные числа при каждом запуске программы.
В предыдущих примерах генерировались целые случайные числа. Мы также можем генерировать случайные числа с плавающей запятой, изменив распределение с uniform_int_distribution
на uniform_real_distribution
. Вот программа, которая демонстрирует, как генерировать случайные числа с плавающей запятой:
int main () { vector<double> numbers; default_random_engine defEngine(time(0)); uniform_real_distribution<double> dblDistro(1.0, 100.0); for (int i = 1; i <= 10; i++) { numbers.push_back(dblDistro(defEngine)); } for (const double n : numbers) { cout << n << " "; } return 0; }
Вот результат одного запуска этой программы:
95.9524 32.255 99.9762 35.4064 67.0245 17.2297 92.0428 99.6441 21.8272 34.0419
Примеры здесь не для профессионалов
Примеры, которые я здесь привел, не должны использоваться в программах, где важна случайность генерируемых данных. Эти примеры предназначены только для генерации случайных чисел, потому что присвоение 100 чисел вектору для проверки функции, использующей данные в векторе, занимает слишком много времени. Проконсультируйтесь со специалистом по случайным числам и статистике, если вам нужны дополнительные советы экспертов по генерации случайных чисел в C ++.
Кроме того, я написал эту статью, потому что несколько людей спросили, следует ли мне использовать в своих примерах старую генерацию случайных чисел в стиле C, и я чувствую, что должен продемонстрировать более современный способ генерации случайных чисел. Для своей работы используйте наиболее удобный для вас метод, но я буду использовать эти более современные методы в своих будущих статьях.
Спасибо, что прочитали эту статью, и пишите мне с комментариями и предложениями.