- 1. үлкен
- 2. QRandomGenerator
- 3. Не істеу?
- 4. Random.hpp
- 5. main.cpp
Кездейсоқ сандарды генерациялау, мысалы, компьютерлік ойында қарудың зақымдалуын есептеу немесе кездейсоқ сандар графигін көрсету үшін қажет болуы мүмкін.
Qt кездейсоқ сандарды генерациялау үшін qrand функциясын қамтамасыз етеді және Qt 5.10 бастап QRandomGenerator класы.
Qt-де кездейсоқ мәндерді қалай алуға болатынын және олардың қаншалықты кездейсоқ екенін көрейік.
үлкен
Біз бастап және дейін мәндер ауқымында сандарды жасаймыз. Ол үшін екі функция жазамыз.
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] ішінде бізге не ұсынылғанын көрейік (https://evileg.com/post/312/)
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-қа қарағанда жақсы жұмыс істейтінін айтады, бірақ іс жүзінде мәселе сол күйінде қалады. Мүмкін, кездейсоқ сандарды генерациялауды үнемі қолданатын бағдарламаның ішінде ұзағырақ тестілеу кезінде сіз айтарлықтай айырмашылықтарды көре аласыз, бірақ мұндай қарапайым нұсқаның өзінде кемшіліктер бар.
Не істеу?
Бір жоба үшін кездейсоқ сандарды генерациялаудың қолайлы шешімін табуға тырысып, мен кездейсоқ сандарды генерациялаудың қолайлы нұсқасы бар C ++ 11 стандарты үшін STD кітапханасында кездейсоқ кітапхана бар екендігі туралы ақпаратты таба алдым. Git Hub сайтында өте қызықты бір кітапхананың іске асырылуы табылды. Осы кітапхананы негізге ала отырып, мен ұрпаққа шағын сынып жаздым (жолдардың арасын оқыңыз, өзім түсіну үшін аздап қайта жаздым).
Random.hpp
Мұнда енгізілген класс белгілі бір ауқымда кездейсоқ мәндерді алу үшін статикалық әдістерді орындау үшін Myers синглтонын пайдаланады. Кездейсоқ сандар генераторларының барлығын инициализациялаудан гөрі бір статикалық класс әдісін дұрыс жерде шақыру әлдеқайда оңай. Сыныптың өзі бүтін сан түрлерімен де, өзгермелі нүкте түрлерімен де жұмыс істейді.
#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 кітапханасынан сілтеме .
А использование функции global() не решает ли эти проблемы?
Получаемая последовательность каждый раз новая.
Дмитрий, решает. Просто автор, видимо, не сильно озаботился изучением документации QRandomGenerator.
Да и в листинге с использованием qrand вызов функции qsrand на каждой итерации цикла наводит на мысль, что автор, мягко говоря, не очень понимает для чего это всё нужно.