Evgenii Legotckoi
Evgenii Legotckoi14 декабря 2017 г. 17:10

Qt/C++ - Урок 074. Генерация псевдослучайных чисел с использованием случайной библиотеки STD

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

Qt предоставляет функцию qrand для генерации случайных чисел, а также, начиная с Qt 5.10, класс QRandomGenerator.

Давайте посмотрим, как в Qt можно получить случайные значения и насколько они случайны.


qrand

Мы будем генерировать числа в диапазоне значений от и до. Для этого напишем две функции.

static int randomBetween(int low, int high)
{
    return (qrand() % ((high + 1) - low) + low);
}

static int randomBetween(int low, int high, int seed)
{
    qsrand(seed); // Setting a base number for counting a random in qrand
    return (qrand() % ((high + 1) - low) + low);
}

Первая функция просто генерирует случайное значение от наименьшего числа до наибольшего. Тогда как во втором с помощью функции qsrand задается базовое число, которое служит основой для генератора псевдослучайных чисел Qt, из которого генерируется число. Это базовое число может быть системным временем в миллисекундах.

Применяем эти функции.

#include <QCoreApplication>
#include <QDateTime>
#include <iostream>

static int randomBetween(int low, int high)
{
    return (qrand() % ((high + 1) - low) + low);
}

static int randomBetween(int low, int high, int seed)
{
    qsrand(seed); // Setting the base number for counting a random host in qrand
    return (qrand() % ((high + 1) - low) + low);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    std::cout << "Random Qt 1 - ";
    for (int i = 0; i < 15; ++i)
    {
        std::cout << randomBetween(15, 43) << " ";
    }
    std::cout << std::endl;

    std::cout << "Random Qt 2 - ";
    for (int i = 0; i < 15; ++i)
    {
        std::cout << randomBetween(15, 43, QDateTime::currentMSecsSinceEpoch()) << " ";
    }
    std::cout << std::endl;

    return a.exec();
}

Получаем следующий вывод.

Random Qt 1 - 15 21 31 27 17 34 43 33 22 32 30 34 35 38 31 
Random Qt 2 - 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 

А теперь проанализируем полученный вывод.

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

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

А теперь посмотрим, что нам предложили в Qt 5.10

QRandomGenerator

Приложение QRandomGenerator будет следующим

#include <QCoreApplication>
#include <iostream>

#include <QRandomGenerator>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    std::cout << "Random Qt 3 - ";
    QRandomGenerator generator;
    for (int i = 0; i < 15; ++i)
    {
        qint64 value = generator.generate() & std::numeric_limits<qint64>::max();
        std::cout << value << " ";
    }
    std::cout << std::endl;

    return a.exec();
}

Вывод будет следующим

Random Qt 3 - 853323747 2396352728 3025954838 2985633182 2815751046 340588426 3587208406 298087538 2912478009 3642122814 3202916223 799257577 1872145992 639469699 3201121432

На самом деле здесь будет повторяться та же проблема, что и с qrand, при повторном запуске программы вы увидите, что числа будут повторяться. В новостях о выпуске Qt 5.10 говорится, что этот класс работает лучше, чем обычный qrand, но на самом деле проблема остается прежней. Возможно, при более длительном тестировании внутри программы, в которой постоянно используется генерация случайных чисел, можно увидеть существенные отличия, но в рамках даже такой простой версии уже есть недостатки.

Что делать?

В попытках найти приемлемое решение для генерации случайных чисел для одного проекта мне удалось найти информацию о том, что в STD Library для стандарта C++11 есть библиотека random, в которой есть более приемлемый вариант генерации случайные числа. Реализация одной очень интересной библиотеки была найдена на Git Hub. На основе этой библиотеки я написал небольшой класс для генерации (читайте между строк, немного переписал для себя, чтобы разобраться).

Random.hpp

Реализованный здесь класс использует синглтон Майерса для выполнения статических методов получения случайных значений в определенном диапазоне. Гораздо проще вызвать один статический метод класса в нужном месте, чем каждый раз инициализировать все генераторы случайных чисел. Сам класс работает как с целыми типами, так и с типами с плавающей запятой.

#ifndef RANDOM_HPP
#define RANDOM_HPP

#include <random>

namespace details
{
    /// True if type T is applicable by a std::uniform_int_distribution
    template<class T>
    struct is_uniform_int {
        static constexpr bool value =
                std::is_same<T,              short>::value ||
                std::is_same<T,                int>::value ||
                std::is_same<T,               long>::value ||
                std::is_same<T,          long long>::value ||
                std::is_same<T,     unsigned short>::value ||
                std::is_same<T,       unsigned int>::value ||
                std::is_same<T,      unsigned long>::value ||
                std::is_same<T, unsigned long long>::value;
    };

    /// True if type T is applicable by a std::uniform_real_distribution
    template<class T>
    struct is_uniform_real {
        static constexpr bool value =
                std::is_same<T,       float>::value ||
                std::is_same<T,      double>::value ||
                std::is_same<T, long double>::value;
    };
}

class Random
{
    template <class T> using IntDist = std::uniform_int_distribution<T>;
    template <class T> using RealDist = std::uniform_real_distribution<T>;

public:
    template <class T>
    static typename std::enable_if<details::is_uniform_int<T>::value, T>::type get(T from = std::numeric_limits<T>::min(), T to = std::numeric_limits<T>::max())
    {
        if (from > to) std::swap(from, to);
        IntDist<T> dist{from, to};
        return dist(instance().engine());
    }

    template <class T>
    static typename std::enable_if<details::is_uniform_real<T>::value, T>::type get(T from = std::numeric_limits<T>::min(), T to = std::numeric_limits<T>::max())
    {
        if (from > to) std::swap(from, to);
        RealDist<T> dist{from, to};
        return dist(instance().engine());
    }

    std::mt19937& engine() { return m_mt; }

protected:
    static Random& instance()
    {
        static Random inst;
        return inst;
    }

private:
    std::random_device m_rd; // Random Number Generator
    std::mt19937 m_mt;       // Standard random number generator

    Random() : m_mt(m_rd()) {}
    ~Random() {}
    Random(const Random&) = delete;
    Random& operator = (const Random&) = delete;
};

#endif // RANDOM_HPP

main.cpp

А теперь приложение

#include <QCoreApplication>

#include "Random.hpp"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    std::cout << "Random STD  - ";
    for (int i = 0; i < 15; ++i)
    {
        std::cout << Random::get(15, 43) << " ";
    }
    std::cout << std::endl;
    return a.exec();
}

Вывод

Random STD  - 38 29 36 38 21 32 33 39 31 15 33 16 36 38 35 

В этом случае мы фактически получим случайный вывод чисел, и числа не будут повторяться при каждом запуске программы. Что касается рандома, то в этом я убедился лично, когда использовал данный класс Random для генерации уровня в игре. Каждый раз расположение предметов в игре не повторялось. Чего было очень сложно добиться с помощью qrand.

Итак, C++11 предоставляет возможности для достаточно качественной генерации псевдослучайных чисел.

А вот ссылка на библиотеку с GitHub , на которой я изучал этот вопрос.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

Дмитрий
  • 29 октября 2020 г. 9:37
  • (ред.)

А использование функции global() не решает ли эти проблемы?

value = QRandomGenerator::global()->bounded(15, 43);

Получаемая последовательность каждый раз новая.

r
  • 17 января 2021 г. 4:09

Дмитрий, решает. Просто автор, видимо, не сильно озаботился изучением документации QRandomGenerator.
Да и в листинге с использованием qrand вызов функции qsrand на каждой итерации цикла наводит на мысль, что автор, мягко говоря, не очень понимает для чего это всё нужно.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50баллов,
  • Очки рейтинга-4
m
  • molni99
  • 26 октября 2024 г. 1:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80баллов,
  • Очки рейтинга4
m
  • molni99
  • 26 октября 2024 г. 1:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20баллов,
  • Очки рейтинга-10
Последние комментарии
ИМ
Игорь Максимов22 ноября 2024 г. 11:51
Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 октября 2024 г. 14:37
Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 октября 2024 г. 8:19
Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5 октября 2024 г. 7:51
Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55 июля 2024 г. 11:02
QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Сейчас обсуждают на форуме
m
moogo22 ноября 2024 г. 7:17
Mosquito Spray System Effective Mosquito Systems for Backyard | Eco-Friendly Misting Control Device & Repellent Spray - Moogo ; Upgrade your backyard with our mosquito-repellent device! Our misters conce…
Evgenii Legotckoi
Evgenii Legotckoi24 июня 2024 г. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 ноября 2024 г. 6:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject4 июня 2022 г. 3:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…

Следите за нами в социальных сетях