В този урок ще разгледаме генерирането на „случайни“ числа предоставено от библиотеката на наследения език C. Ще разгледаме функцийте srand() и rand().
Какво представляват псевдо случайните числа?
Псевдо случайните числа са числа, които изглеждат случайни, но всъщност се генерират от детерминиран алгоритъм. Това означава, че ако знаем началното състояние (т.нар. seed), можем да възпроизведем същата последователност. При всяка различна инициализация на алгоритъма се генерират различни последователности.
В съвременния C++ има по добър алгоритъм. Но него няма да го разглеждаме сега. Въвеждаме този урок допълнително във връзка с финалните задачи от „Въведение в езика C++„. Правим това, за да не товарим излишно потребителя с въвеждането на данни когато стартира решение на някоя задача. Намирам това за досадно.
Какво е PRNG алгоритъм?
Превежда се: Pseudorandom number generator. Или генератор за псевдо случайни числа. Този клас алгоритми са полезни за:
- Симулации;
- Игри;
- Тестове на други алгоритми;
PRNG не са криптографски устойчиви. Генерираната последователност може да бъде предвидена. Ако знаем с каква стойност е инициализиран алгоритъма. Самата инициализация се нарича посяване (seed) понякога.
Стартирайте няколко пъти тази програма:
#include <iostream>
#include <ctime>
#include <cstdlib>
int main() {
srand(42); // инициализация
for(int i = 1; i <= 5; i++) {
// случайно число
int value = rand();
std::cout << value << std::endl;
}
}Винаги получаваме един и същи изход. За да не се случва това всеки път при стартиране на този код трябва да задаваме различна стойност на srand().
Най често това став с time(0). Тази функция връща текущото време на системата в секунди. Времеви маркер.
srand( (unsigned) time(0) ); // инициализацияИ така програмата ни придобива вида:
#include <iostream>
#include <ctime>
#include <cstdlib>
int main() {
srand( (unsigned) time(0) );
for(int i = 1; i <= 5; i++) {
int value = rand();
std::cout << value << std::endl;
}
}На ред 6 в кода използваме експлицитно (явно) преобразуване на типа, за да избегнем мрънкането на компилатора. Стартирайте тази програма няколко пъти и вижте изхода и. Генерираната последователност е винаги различна.
Нужно е да инициализираме RPNG алгоритъма само веднъж преди да го използваме. Повторна инициализация може да доведе до съвпадащи резултати.
Функцията srand()
Инициализира генератора. Намира се в библиотеката <cstdlib>. Декларация:
void srand(unsigned seed);Функцията rand()
Генерира псевдо случайно число в интервала 0 ≤ rand() ≤ RAND_MAX.
Намира се в <cstdlib>. Декларация:
int rand();Функцията time()
Връща времеви маркер за текущото време. Намира се в <ctime>. Това са секунди от епохата записани в 64 битов int (long long). Декларация:
time_t time(time_t* timer);
// time_t ~ long longОграничаване на интервала
Както казахме върнатия резултат е в интервала 0 ≤ rand() ≤ RAND_MAX (int). Но всъщност можем да връщаме случайни числа в различни диапазони.
Дробно число между 0 и 1:
double x = (double) rand() / RAND_MAX;Дробно число между a и b:
double x = a + (double) rand() / RAND_MAX * (b - a);Случайно дробно число:
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cmath>
int main() {
srand( (unsigned) time(0) );
int a = rand();
int b = rand();
// дава броя на десетичните цифри на b
int digits = log10(b) + 1;
// "слепва" ги
double x = a + b / pow(10, digits);
std::cout << x << std::endl;
}Цяло число от a до b:
int x = a + rand() % (b - a + 1);Примерна реализация на PRNG
Ето как би изглеждал кода на примерен генератор на случайни числа. Силно ви препоръчвам да не пишете свой реализации. А да ползвате функциите от C/C++ библиотеката. В долния код важното е в ред 10. Но такива формули се пишат от професионалисти и щателно се тестват и проверяват. Приемете, че в стандартната библиотека имплементациите са едни от най – добрите. Ползвайте нея.
#include <iostream>
#include <ctime>
using namespace std;
// начална стойност
long long seed = 123456789 + time(0);
int randomInt(int min, int max) {
// LCG формула
seed = (1103515245 * seed + 12345) % (1LL << 31);
return min + (seed % (max - min + 1));
}
int main() {
for (int i = 0; i < 10; i++) {
cout << randomInt(1, 100) << endl;
}
}