Evgenii Legotckoi
Evgenii Legotckoi14. Dezember 2017 17:10

Qt/C++ - Tutorial 074. Generieren von Pseudo-Zufallszahlen mit der STD-Bibliothek random

Die Generierung von Zufallszahlen kann beispielsweise erforderlich sein, um den Waffenschaden in einem Computerspiel zu berechnen oder einen Graphen aus Zufallszahlen darzustellen.

Qt bietet die Funktion qrand zum Generieren von Zufallszahlen und seit Qt 5.10 die Klasse QRandomGenerator.

Mal sehen, wie zufällige Werte in Qt erhalten werden können und wie zufällig sie sind.


qrand

Wir werden Zahlen im Wertebereich von und bis generieren. Dazu schreiben wir zwei Funktionen.

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);
}

Die erste Funktion generiert einfach einen Zufallswert von der kleinsten Zahl zur größten. Während im zweiten Fall mit der qsrand-Funktion eine Basiszahl gesetzt wird, die als Grundlage für den Qt-Pseudozufallszahlengenerator dient, aus dem die Zahl generiert wird. Diese Basiszahl kann die Systemzeit in Millisekunden sein.

Lassen Sie uns diese Funktionen verwenden.

#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();
}

Wir erhalten die folgende Ausgabe.

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 

Lassen Sie uns nun die resultierende Ausgabe analysieren.

Im ersten Fall stellte sich heraus, dass die Zahlen zufällig waren ... Und wie viele? Wenn Sie versuchen, das Programm mehrmals hintereinander auszuführen, werden Sie feststellen, dass die Zahlen jedes Mal gleich sind, was nicht sehr gut und offensichtlich nicht sehr zufällig ist.

Bei der zweiten Option stellten sich hier alle Zahlen als gleich heraus. Die Sache ist, dass wir jedes Mal versucht haben, die Basisnummer auf dieselbe Nummer einzustellen. Schließlich führt ein Prozessor mit einer Frequenz von einigen GHz diesen einfachen Zyklus sehr schnell aus, was bedeutet, dass die Zeit in Millisekunden einfach keine Zeit hat, sich zu ändern, was bedeutet, dass die Basiszahl dieselbe ist. Und wenn Sie die Basisnummer festlegen, wird die Generierung von Anfang an durchgeführt, in diesem Fall werden Sie sehen, dass es nicht sehr zufällig ist.

Und jetzt sehen wir uns an, was uns in Qt [5.10] geboten wurde (https://evileg.com/post/312/)

QRandomGenerator

Die QRandomGenerator-App sieht wie folgt aus

#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();
}

Die Ausgabe wird wie folgt sein

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

Tatsächlich wird sich hier das gleiche Problem wie bei qrand wiederholen, wenn Sie das Programm erneut ausführen, werden Sie sehen, dass sich die Zahlen wiederholen. Qt 5.10 Release News sagt, dass diese Klasse besser funktioniert als normale qrand, aber tatsächlich bleibt das Problem das gleiche. Vielleicht sieht man bei längerem Testen innerhalb eines Programms, das ständig Zufallszahlengenerierung verwendet, deutliche Unterschiede, aber selbst innerhalb einer so einfachen Version gibt es schon Nachteile.

Was zu tun ist?

Bei dem Versuch, eine akzeptable Lösung zum Generieren von Zufallszahlen für ein Projekt zu finden, habe ich Informationen gefunden, dass es in der STD-Bibliothek für den C ++ 11-Standard eine Zufallsbibliothek gibt, die eine akzeptablere Option zum Generieren von Zufallszahlen bietet. Die Implementierung einer sehr interessanten Bibliothek wurde auf Git Hub gefunden. Basierend auf dieser Bibliothek habe ich eine kleine Generationsklasse geschrieben (zwischen den Zeilen lesen, ich habe ein wenig umgeschrieben, damit ich es verstehe).

Zufällig.hpp

Die hier implementierte Klasse verwendet ein Myers-Singleton, um statische Methoden auszuführen, um Zufallswerte innerhalb eines bestimmten Bereichs zu erhalten. Es ist viel einfacher, eine statische Klassenmethode an der richtigen Stelle aufzurufen, als jedes Mal alle Zufallszahlengeneratoren zu initialisieren. Die Klasse selbst arbeitet sowohl mit Integer- als auch mit Fließkommatypen.

#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

Und jetzt die Bewerbung

#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();
}

Fazit

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

In diesem Fall erhalten wir tatsächlich eine zufällige Ausgabe von Zahlen, und die Zahlen werden nicht jedes Mal wiederholt, wenn das Programm ausgeführt wird. Was die Zufälligkeit betrifft, so war ich persönlich davon überzeugt, als ich diese Zufallsklasse verwendet habe, um ein Level im Spiel zu generieren. Jedes Mal, wenn die Position von Objekten im Spiel nicht wiederholt wurde. Was mit qrand sehr schwer zu erreichen war.

C++11 bietet also Möglichkeiten für die Generierung von Pseudozufallszahlen mit ziemlich hoher Qualität.

Und hier ist Link zur Bibliothek von GitHub , auf der ich dieses Problem studiert habe.

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

Дмитрий
  • 29. Oktober 2020 09:37
  • (bearbeitet)

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

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

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

r
  • 17. Januar 2021 04:09

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

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
A
ALO1ZE19. Oktober 2024 08:19
Fb3-Dateileser auf Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken