cout << "weibull_distribution\n";
print_distro(weibull_distribution
cout << "extreme_value_distribution\n";
print_distro(
extreme_value_distribution{0.0, 1.0}, samples);
cout << "lognormal_distribution\n";
print_distro(lognormal_distribution
cout << "chi_squared_distribution\n";
print_distro(chi_squared_distribution
cout << "cauchy_distribution\n";
print_distro(cauchy_distribution
cout << "fisher_f_distribution\n";
print_distro(fisher_f_distribution
cout << "student_t_distribution\n";
print_distro(student_t_distribution
}
13. Компиляция и запуск программы дадут следующий результат. Сначала запустим программу с 1000 образцами для каждого распределения (рис. 8.7).
14. Еще один запуск, на этот раз с 1 000 000 образцов для каждого распределения, покажет, что гистограммы выглядят гораздо чище и более характерно для каждого из них. Кроме того, мы увидим, какие распределения генерируются медленно, а какие — быстро (рис. 8.8).
Как это работает
Хотя генераторы случайных чисел нас не интересуют до тех пор, пока работают быстро и создают числа максимально случайным образом, нам
Чтобы использовать любое распределение, сначала нужно создать для него соответствующий объект. Мы видели, что разные распределения принимают разные аргументы конструктора. В описании примера мы кратко остановились на некоторых видах распределения, поскольку большинство из них слишком специфичны и/или сложны, чтобы рассматривать их здесь. Не волнуйтесь, все они подробно описаны в документации к C++ STL.
Однако, как только появляется экземпляр распределения, можно вызвать его как функцию, которая принимает в качестве единственного параметра объект генератора случайных чисел. Далее объект распределения получает случайное число, придает ему некую форму (которая полностью зависит от выбранного распределения), а затем возвращает его нам. Это приводит к появлению совершенно разных гистограмм, что мы видели после запуска программы.
Программа, которую мы только что написали, позволит нам получить наиболее полную информацию о разных распределениях. В дополнение к этому рассмотрим самые важные виды распределения (табл. 8.2). Для всех остальных видов распределения вы можете обратиться к документации C++ STL.
Глава 9
Параллелизм и конкурентность
В этой главе:
□ автоматическое распараллеливание кода, использующего стандартные алгоритмы;
□ приостановка программы на конкретный промежуток времени;
□ запуск и приостановка потоков;
□ выполнение устойчивой к исключениям общей блокировки с помощью std::unique_lock
std::shared_lock
;□ избегание взаимных блокировок с применением std::scoped_lock
□ синхронизация конкурентного использования std::cout
□ безопасное откладывание инициализации с помощью std::call_once
□ отправка выполнения задач в фоновый режим с применением std::async
□ реализация идиомы «производитель/потребитель» с использованием std::condition_variable
□ реализация идиомы «несколько потребителей/производителей» с помощью std::condition_variable
□ распараллеливание отрисовщика множества Мандельброта в ASCII с применением std::a
□ реализация небольшой автоматической библиотеки для распараллеливания с использованием std::future
Введение
До C++11 язык C++ не поддерживал параллельные вычисления. Это не значило, что запуск, управление, остановка и синхронизация потоков были невыполнимы, но для каждой операционной системы требовались специальные библиотеки, поскольку потоки по своей природе связаны с ОС.
С появлением C++11 мы получили библиотеку std::thread
std::condition_variable
позволяет отправлять гибкие уведомления о событиях между потоками.