- 1. qrand
- 2. QRandomGenerator
- 3. Was zu tun ist?
- 4. Zufällig.hpp
- 5. main.cpp
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.
А использование функции global() не решает ли эти проблемы?
Получаемая последовательность каждый раз новая.
Дмитрий, решает. Просто автор, видимо, не сильно озаботился изучением документации QRandomGenerator.
Да и в листинге с использованием qrand вызов функции qsrand на каждой итерации цикла наводит на мысль, что автор, мягко говоря, не очень понимает для чего это всё нужно.