Читать книги » Книги » Компьютеры и Интернет » Программирование » C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Читать книгу C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц, Яцек Галовиц . Жанр: Программирование.
C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц
Название: C++17 STL Стандартная библиотека шаблонов
Дата добавления: 15 июль 2023
Количество просмотров: 1 471
(18+) Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних просмотр данного контента СТРОГО ЗАПРЕЩЕН! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту для удаления материала.
Читать онлайн

C++17 STL Стандартная библиотека шаблонов читать книгу онлайн

C++17 STL Стандартная библиотека шаблонов - читать онлайн , автор Яцек Галовиц

С++ — объектно-ориентированный язык программирования, без которого сегодня немыслима промышленная разработка ПО. В этой замечательной книге описана работа с контейнерами, алгоритмами, вспомогательными классами, лямбда-выражениями и другими интересными инструментами, которыми богат современный С++. Освоив материал, вы сможете коренным образом пересмотреть привычный подход к программированию.
Преимущество издания — в подробном описании стандартной библиотеки шаблонов С++, STL. Ее свежая версия была выпущена в 2017 году. В книге вы найдете более 90 максимально реалистичных примеров, которые демонстрируют всю мощь STL. Многие из них станут базовыми кирпичиками для решения более универсальных задач.
Вооружившись этой книгой, вы сможете эффективно использовать С++17 для создания высококачественного и высокопроизводительного ПО, применимого в различных отраслях.

1 ... 94 95 96 97 98 ... 121 ВПЕРЕД
Перейти на страницу:
представить результат броска. Для выполнения таких простых задач не нужно использовать библиотеку.

А если мы хотим смоделировать событие, которое случается с вероятностью 66%? Окей, можно создать формулу наподобие bool yesno = (rand()%100>66). (Погодите, нам следует использовать оператор >= или правильнее будет оставить оператор >?)

Кроме того, как смоделировать бросок нечестного кубика, грани которого могут выпасть с разной вероятностью? Как смоделировать более сложные распределения? Такие задачи могут быстро перерасти в научные. Чтобы сконцентрироваться на наших основных задачах, взглянем на инструменты, предоставляемые STL.

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

Как это делается

В этом примере мы будем генерировать случайные числа, придавать им форму и выводить на экран шаблоны распределения. Таким образом, рассмотрим их все и разберем их самые важные свойства, которые могут оказаться полезными, если потребуется смоделировать что-то конкретное.

1. Сначала включим все необходимые заголовочные файлы и объявим об использовании пространства имен std:

#include <iostream>

#include <iomanip>

#include <random>

#include <map>

#include <string>

#include <algorithm>

using namespace std;

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

template <typename T>

void print_distro(T distro, size_t samples)

{

  default_random_engine e;

  map<int, size_t> m;

3. Возьмем столько образцов, сколько указано в переменной samples, и заполним ими ассоциативный массив счетчиков. Таким образом получим очередную гистограмму. Простой вызов e() даст необработанное простое число, distro(e) придает случайным числам форму с помощью объекта распределения:

  for (size_t i {0}; i < samples; ++i) {

    m[distro(e)] += 1;

  }

4. Чтобы получить выходные данные, которые помещаются в окно консоли, нужно узнать самое большое значение счетчика. Функция max_element поможет определить такое значение путем сравнения всех связанных счетчиков в массиве и возвращения итератора, указывающего на узел, содержащий данное значение. Зная это значение, можем определить, на какое число следует разделить все значения счетчиков, чтобы уместить полученный результат в окно консоли.

  size_t max_elm (max_element(begin(m), end(m),

    [](const auto &a, const auto &b) {

      return a.second < b.second;

    })->second);

  size_t max_div (max(max_elm / 100, size_t(1)));

5. Теперь пройдем по массиву в цикле и выведем полоски из символов '*' для всех счетчиков большого размера. Остальные значения отбросим, поскольку некоторые генераторы случайных чисел распределяют числа так широко, что это переполнит наши окна консоли.

  for (const auto [randval, count] : m) {

    if (count < max_elm / 200) { continue; }

    cout << setw(3) << randval << " : "

         << string(count / max_div, '*') << 'n';

  }

}

6. В функции main проверим, предоставил ли пользователь ровно один параметр, который указывает, сколько именно образцов нужно взять из каждого распределения. Если пользователь передал ноль или несколько параметров, то сгенерируем ошибку:

int main(int argc, char **argv)

{

  if (argc != 2) {

    cout << "Usage: " << argv[0]

         << " <samples>n"; return 1;

  }

7. Теперь преобразуем аргумент командной строки в число с помощью вызова std::stoull:

  size_t samples {stoull(argv[1])};

8. Сначала попробуем распределения uniform_int_distribution и normal_distribution. Они используются в большинстве случаев, когда нужно применить генератор случайных чисел. Все, кто когда-то изучал стохастику в университете, скорее всего, слышали о них. Равномерное распределение принимает два значения, указывая нижнюю и верхнюю границы диапазона, в котором будут распределены случайные значения. Выбрав 0 и 9, мы получим одинаково часто встречающиеся значения между 0 и 9 (включительно). Нормальное распределение принимает в качестве аргументов математическое ожидание и среднеквадратическое отклонение.

  cout << "uniform_int_distributionn";

  print_distro(uniform_int_distribution<int>{0, 9}, samples);

  cout << "normal_distributionn";

  print_distro(normal_distribution<double>{0.0, 2.0}, samples);

9. Еще одним очень интересным распределением является piecewise_constant_distribution. Оно принимает в качестве аргументов два входных диапазона. Первый диапазон содержит числа, которые указывают границы интервалов. Определив их как 0, 5, 10, 30, получим три интервала, простирающиеся от 0 до 4, от 5 до 9 и от 10 до 29. Еще один входной диапазон определяет веса входных диапазонов. Установив значения этих весов равными 0.2, 0.3, 0.5, мы укажем, что из соответствующих интервалов случайные числа будут получены с вероятностями 20, 30 и 50%. Внутри каждого из интервалов значения будут иметь одинаковую вероятность выпадения.

  initializer_list<double> intervals {0, 5, 10, 30};

  initializer_list<double> weights {0.2, 0.3, 0.5};

  cout << "piecewise_constant_distributionn";

  print_distro(

    piecewise_constant_distribution<double>{

      begin(intervals), end(intervals),

      begin(weights)},

    samples);

10. Распределение piecewise_linear_distribution создается аналогично, но веса работают совершенно по-другому. Для каждой граничной точки интервала существует одно значение веса. При переходе от одной границы к другой вероятность интерполируется линейно. Воспользуемся теми же интервалами, но передадим другой список весов:

  cout << "piecewise_linear_distributionn";

  initializer_list<double> weights2 {0, 1, 1, 0};

  print_distro(

    piecewise_linear_distribution<double>{

      begin(intervals), end(intervals), begin(weights2)},

    samples);

11. Распределение Бернулли — это еще одно важное распределение, поскольку распределяет лишь значения «да/нет», «попадание/промах» или «орел/решка» с конкретной вероятностью. Его выходными значениями будут только 0 и 1. Еще одним интересным распределением, полезным во многих случаях, является discrete_distribution. В нашем случае инициализируем его дискретными значениями 1, 2, 4, 8. Они интерпретируются как веса для возможных выходных значений от 0 до 3.

  cout <<

1 ... 94 95 96 97 98 ... 121 ВПЕРЕД
Перейти на страницу:
Комментарии (0)