Last modified: September 25, 2024
This article is written in: 馃嚨馃嚤
W j臋zyku C++ liczby losowe generuje si臋 za pomoc膮 standardowej biblioteki <random>
. Proces losowania zaczyna si臋 od utworzenia generatora liczb pseudolosowych, np. std::mt19937
, kt贸ry bazuje na algorytmie Mersenne Twister. Aby uzyska膰 bardziej losowe wyniki, generator inicjalizuje si臋 za pomoc膮 unikalnej warto艣ci, zwanej "ziarnem" (ang. seed), co mo偶na zrobi膰 np. poprzez std::random_device
. Nast臋pnie u偶ywa si臋 odpowiednich dystrybucji, takich jak std::uniform_int_distribution
(dla liczb ca艂kowitych z r贸wnomiernym rozk艂adem) lub std::uniform_real_distribution
(dla liczb zmiennoprzecinkowych), aby wygenerowa膰 liczby z okre艣lonego zakresu. Dzi臋ki tej bibliotece losowanie w C++ jest bardziej elastyczne i daje kontrol臋 nad r贸偶nymi aspektami generowania liczb losowych, w tym nad zakresem i typem warto艣ci.
Liczby losowe
Liczby losowe to warto艣ci numeryczne generowane w taki spos贸b, 偶e nie da si臋 przewidzie膰 ich kolejno艣ci czy warto艣ci bez znajomo艣ci wewn臋trznych mechanizm贸w generowania. W kontek艣cie informatyki i matematyki, liczby te s膮 niezb臋dne w wielu zastosowaniach, takich jak symulacje komputerowe, kryptografia, statystyka czy gry losowe.
Pseudolosowo艣膰 a prawdziwa losowo艣膰
Warto zauwa偶y膰, 偶e komputery, b臋d膮c maszynami deterministycznymi, nie s膮 w stanie generowa膰 prawdziwie losowych liczb bez zewn臋trznego 藕r贸d艂a entropii. Dlatego te偶 korzystaj膮 z algorytm贸w generuj膮cych liczby pseudolosowe (PRNG - Pseudorandom Number Generator). Algorytmy te produkuj膮 sekwencje liczb, kt贸re wygl膮daj膮 na losowe i spe艂niaj膮 pewne statystyczne w艂asno艣ci losowo艣ci, ale s膮 deterministyczne i powtarzalne, je艣li znany jest ich stan pocz膮tkowy (tzw. ziarno - seed).
Zastosowania liczb losowych
Liczby losowe s膮 kluczowe w:
- Symulacjach komputerowych do modelowania zjawisk losowych w fizyce, chemii czy ekonomii.
- Kryptografii do generowania kluczy szyfruj膮cych, wektor贸w inicjuj膮cych i innych element贸w bezpiecze艅stwa.
- Grach komputerowych do tworzenia nieprzewidywalnych zachowa艅 przeciwnik贸w czy losowych zdarze艅.
- Metodach Monte Carlo w numerycznej integracji i optymalizacji.
- Testowaniu i walidacji do tworzenia losowych danych testowych.
Generowanie liczb losowych w C++
W j臋zyku C++ od wersji C++11 wprowadzono nowoczesne narz臋dzia do generowania liczb pseudolosowych w postaci biblioteki <random>
. Biblioteka ta oferuje r贸偶norodne generator贸w i dystrybucji, pozwalaj膮c na precyzyjn膮 kontrol臋 nad procesem generowania.
Generatory i dystrybucje
Proces generowania liczb losowych w C++ opiera si臋 na dw贸ch elementach:
- Generator liczb losowych to algorytm dostarczaj膮cy sekwencj臋 losowych bit贸w. Przyk艂ady to
std::mt19937
(Mersenne Twister) czystd::random_device
. - Dystrybucja to funkcja przekszta艂caj膮ca losowe bity z generatora na liczby w okre艣lonym rozk艂adzie (np. r贸wnomiernym, normalnym, Poissona).
Inicjalizacja generatora
Wa偶ne jest, aby generator zosta艂 zainicjalizowany odpowiednim ziarnem, co zapewnia r贸偶norodno艣膰 generowanych sekwencji. std::random_device
mo偶e by膰 u偶yty do pobrania entropii z systemu operacyjnego, co jest szczeg贸lnie istotne w aplikacjach kryptograficznych.
Przyk艂ad funkcji losowa_z_przedzialu()
:
#include <random>
int losowa_z_przedzialu(int start, int end) {
// Inicjalizacja generatora liczb losowych
std::random_device rd; // 殴r贸d艂o prawdziwej entropii (je艣li dost臋pne)
std::mt19937 gen(rd()); // Mersenne Twister PRNG, zainicjalizowany za pomoc膮 rd()
std::uniform_int_distribution<> dist(start, end); // Dystrybucja r贸wnomierna na przedziale [start, end]
return dist(gen); // Generowanie liczby losowej
}
W powy偶szym kodzie:
std::random_device rd;
s艂u偶y do pobrania losowego ziarna z systemu.std::mt19937 gen(rd());
tworzy generator Mersenne Twister z ziarnard()
.std::uniform_int_distribution<> dist(start, end);
definiuje dystrybucj臋 r贸wnomiern膮 ca艂kowitoliczbow膮.return dist(gen);
zwraca liczb臋 losow膮 z okre艣lonego przedzia艂u.
Przyk艂ady zastosowania
Rzut monet膮
Symulacja rzutu monet膮 jest klasycznym przyk艂adem wykorzystania liczb losowych. Moneta ma dwa mo偶liwe wyniki: orze艂 lub reszka, co odpowiada zdarzeniu losowemu z prawdopodobie艅stwem 0.5 dla ka偶dego wyniku.
#include <iostream>
#include <random>
bool orzel_lub_reszka() {
// U偶ycie dystrybucji Bernoulliego
std::random_device rd;
std::mt19937 gen(rd());
std::bernoulli_distribution dist(0.5); // Prawdopodobie艅stwo sukcesu 0.5
return dist(gen);
}
int main() {
if (orzel_lub_reszka()) {
std::cout << "Orze艂" << std::endl;
} else {
std::cout << "Reszka" << std::endl;
}
return 0;
}
- U偶ywamy
std::bernoulli_distribution
, kt贸ra zwracatrue
z okre艣lonym prawdopodobie艅stwem. - Generator
gen
jest zainicjalizowany z ziarnem zstd::random_device
.
Rzut kostk膮
Symulacja rzutu sze艣cio艣cienn膮 kostk膮 wymaga generowania liczb ca艂kowitych z przedzia艂u [1,6], gdzie ka偶da liczba ma jednakowe prawdopodobie艅stwo wyst膮pienia.
#include <iostream>
#include <random>
int rzut_kostka() {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(1, 6); // Dystrybucja r贸wnomierna ca艂kowitoliczbowa
return dist(gen);
}
int main() {
std::cout << "Wynik rzutu kostk膮: " << rzut_kostka() << std::endl;
return 0;
}
- U偶ywamy dystrybucji r贸wnomiernej ca艂kowitoliczbowej na przedziale [1,6].
- Ka偶dy wynik ma prawdopodobie艅stwo 1/6.
Generator hase艂
Generowanie silnych, losowych hase艂 jest kluczowe dla bezpiecze艅stwa danych. W tym przyk艂adzie tworzymy has艂o o okre艣lonej d艂ugo艣ci, sk艂adaj膮ce si臋 z losowo wybranych znak贸w z predefiniowanego zestawu.
#include <iostream>
#include <string>
#include <random>
std::string generuj_haslo(int dlugosc) {
const std::string zestaw_znakow = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+";
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(0, zestaw_znakow.size() - 1);
std::string haslo;
for(int i = 0; i < dlugosc; i++) {
haslo += zestaw_znakow[dist(gen)];
}
return haslo;
}
int main() {
int dlugosc_hasla = 12;
std::cout << "Wygenerowane has艂o: " << generuj_haslo(dlugosc_hasla) << std::endl;
return 0;
}
- U偶ywamy dystrybucji r贸wnomiernej do wyboru indeksu znaku z
zestaw_znakow
. - Ka偶dy znak w ha艣le jest losowo wybrany, co zwi臋ksza entropi臋 has艂a.
Zastosowanie r贸偶nych dystrybucji
Biblioteka <random>
oferuje r贸偶ne dystrybucje, kt贸re pozwalaj膮 generowa膰 liczby losowe zgodnie z okre艣lonymi rozk艂adami statystycznymi.
Dystrybucja r贸wnomierna
Dystrybucja r贸wnomierna zapewnia jednakowe prawdopodobie艅stwo wyst膮pienia ka偶dej warto艣ci w okre艣lonym przedziale.
#include <iostream>
#include <random>
int main() {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(1, 100);
std::cout << "Przyk艂adowe liczby z dystrybucji r贸wnomiernej:" << std::endl;
for (int i = 0; i < 10; ++i) {
std::cout << dist(gen) << " ";
}
std::cout << std::endl;
return 0;
}
Dystrybucja normalna (Gaussa)
Dystrybucja normalna, zwana r贸wnie偶 rozk艂adem Gaussa, jest powszechnie stosowana w statystyce i modelowaniu naturalnych zjawisk.
#include <iostream>
#include <random>
int main() {
std::random_device rd;
std::mt19937 gen(rd());
std::normal_distribution<> dist(50, 10); // 艢rednia 50, odchylenie standardowe 10
std::cout << "Przyk艂adowe liczby z dystrybucji normalnej:" << std::endl;
for (int i = 0; i < 10; ++i) {
double liczba = dist(gen);
std::cout << liczba << " ";
}
std::cout << std::endl;
return 0;
}
- Liczby generowane s膮 wok贸艂 艣redniej 50 z okre艣lonym odchyleniem standardowym.
- Rozk艂ad normalny jest symetryczny i opisuje wiele zjawisk naturalnych.
Dystrybucja Bernoulliego
Dystrybucja Bernoulliego opisuje zdarzenia dwuwarto艣ciowe (sukces/pora偶ka) z okre艣lonym prawdopodobie艅stwem sukcesu p
.
#include <iostream>
#include <random>
int main() {
std::random_device rd;
std::mt19937 gen(rd());
double p = 0.3; // Prawdopodobie艅stwo sukcesu
std::bernoulli_distribution dist(p);
std::cout << "Przyk艂adowe wyniki z dystrybucji Bernoulliego (p = " << p << "):" << std::endl;
for (int i = 0; i < 10; ++i) {
bool wynik = dist(gen);
std::cout << wynik << " "; // 1 - sukces, 0 - pora偶ka
}
std::cout << std::endl;
return 0;
}
- Przydatne w modelowaniu zdarze艅 typu "tak/nie".
- Suma wielu zmiennych Bernoulliego prowadzi do rozk艂adu dwumianowego.
Zalety i wady r贸偶nych metod
Korzystanie z std::random
Zalety:
- Generatory takie jak
std::mt19937
(Mersenne Twister) oferuj膮 wysok膮 jako艣膰 liczb pseudolosowych z d艂ugim okresem powtarzalno艣ci. - Mo偶liwo艣膰 wyboru spo艣r贸d wielu predefiniowanych dystrybucji.
- Precyzyjna kontrola nad ziarniem i parametrami generatora.
- Uniwersalno艣膰 i przeno艣no艣膰 kodu.
Wady:
- Wi臋ksza liczba klas i funkcji mo偶e by膰 trudniejsza w u偶yciu dla pocz膮tkuj膮cych.
- Nieco mniejsza wydajno艣膰 w por贸wnaniu z prostszymi metodami, cho膰 w praktyce r贸偶nice s膮 cz臋sto nieistotne.
Por贸wnanie z rand()
rand()
to starsza funkcja z biblioteki C, kt贸ra jest nadal dost臋pna w C++.
#include <iostream>
#include <cstdlib>
#include <ctime>
int main() {
std::srand(std::time(nullptr)); // Inicjalizacja ziarna
for (int i = 0; i < 10; ++i) {
std::cout << std::rand() % 100 << " ";
}
std::cout << std::endl;
return 0;
}
Zalety rand()
:
- 艁atwo艣膰 u偶ycia dla prostych zastosowa艅.
- Dost臋pna we wszystkich kompilatorach zgodnych z C i C++.
Wady rand()
:
- Kr贸tki okres powtarzalno艣ci i s艂aba losowo艣膰, co mo偶e prowadzi膰 do przewidywalnych sekwencji.
- Ograniczona mo偶liwo艣膰 konfiguracji generatora i brak wsparcia dla r贸偶nych dystrybucji.
- Implementacja
rand()
mo偶e si臋 r贸偶ni膰 mi臋dzy kompilatorami.