Generowanie liczb losowych w C++

Funkcje rand() i srand() są częścią biblioteki C standardowej (<cstdlib> w C++), służącej do generowania pseudolosowych liczb całkowitych. Są to podstawowe narzędzia do pracy z prostymi potrzebami losowości w programach C i C++.

rand()

Funkcja rand() zwraca pseudolosową liczbę całkowitą w zakresie od 0 do RAND_MAX, gdzie RAND_MAX jest stałą zdefiniowaną w <cstdlib> i zazwyczaj wynosi co najmniej 32767 (2^15 – 1). Sygnatura funkcji:
int rand(void);
Przykład użycia:
#include <iostream>
#include <cstdlib> // Dla rand()

int main() {
    // Wygenerowanie i wypisanie pseudolosowej liczby
    std::cout << "Losowa liczba: " << rand() << std::endl;
    return 0;
}

srand()

Funkcja srand() służy do inicjalizacji generatora liczb pseudolosowych używanego przez rand(). Pozwala to na kontrolę nad sekwencją pseudolosowych liczb generowanych przez rand(). Jeśli srand() nie zostanie wywołana przed rand(), generator jest automatycznie inicjalizowany wartością 1. Sygnatura funkcji:
void srand(unsigned int seed);
Parametr seed jest „ziarnem” inicjującym generator, które determinuje sekwencję generowanych liczb. Używając tego samego ziarna, zawsze uzyskamy tę samą sekwencję liczb z rand(). Przykład użycia:
#include <iostream>
#include <cstdlib> // Dla rand(), srand()
#include <ctime>   // Dla time()

int main() {
    // Inicjalizacja generatora aktualnym czasem
    srand(static_cast<unsigned int>(time(nullptr)));

    // Wygenerowanie i wypisanie pseudolosowej liczby
    std::cout << "Losowa liczba: " << rand() << std::endl;
    return 0;
}

Kluczowe informacje

  • Pseudolosowość: Zarówno rand() jak i srand() generują liczby pseudolosowe, co oznacza, że sekwencja jest deterministyczna i może być powtórzona przez użycie tego samego ziarna w srand().
  • Portowalność: Chociaż rand() i srand() są dostępne w większości środowisk C i C++, jakość i zakres generowanych liczb mogą się różnić między implementacjami.
  • Wady: Jedną z głównych wad rand() jest jej potencjalna niejednorodność rozkładu i ograniczenia związane z RAND_MAX. Dla bardziej zaawansowanych potrzeb, zaleca się korzystanie z funkcji dostępnych w <random>.

Wskazówka

Dla nowoczesnych aplikacji C++ zalecane jest użycie funkcji i klas z biblioteki <random>, która oferuje większą elastyczność, lepszy rozkład i wsparcie dla pracy wielowątkowej. Funkcje rand() i srand() mogą być użyteczne dla prostych zastosowań lub w kontekście utrzymania starszego kodu.
<random> vs rand() i srand()
W C++ istnieją dwa główne podejścia do generowania liczb losowych: nowoczesne API z biblioteki <random> (dostępne od C++11) oraz starsze funkcje rand() i srand() z biblioteki <cstdlib>.
Biblioteka <random> (C++11 i nowsze)
Biblioteka <random> została wprowadzona w C++11, oferując bogaty zestaw narzędzi do generowania liczb losowych. Pozwala na wybór spośród różnych generatorów liczb losowych oraz rozkładów probabilistycznych, co zapewnia większą elastyczność i dokładność niż tradycyjne podejście z rand(). Kluczowe cechy:
  • Różne generatory liczb losowych: np. std::mt19937 (generator Mersenne Twister).
  • Różne rozkłady: np. std::uniform_int_distribution, std::normal_distribution.
  • Większa kontrola i dokładność: umożliwia precyzyjne określenie charakterystyki generowanych liczb.
Przykład użycia <random>:
#include <iostream>
#include <random>

int main() {
    std::mt19937 gen(std::random_device{}()); // Inicjalizacja generatora Mersenne Twister
    std::uniform_int_distribution<int> distrib(1, 6); // Rozkład jednostajny dla kostki do gry

    for(int n = 0; n < 10; ++n) {
        std::cout << distrib(gen) << ' '; // Generowanie i wypisywanie liczb losowych
    }
    std::cout << '\n';

    return 0;
}
Funkcje rand() i srand() z <cstdlib>
Przed wprowadzeniem C++11, standardowym sposobem generowania liczb losowych w C++ było użycie funkcji rand(), opcjonalnie z inicjalizacją generatora liczb losowych za pomocą srand(). Kluczowe cechy:
  • Simplicity: Łatwe w użyciu, ale oferuje mniej kontroli niż <random>.
  • Globalny stan: rand() korzysta z globalnego stanu generatora, co może być problematyczne w aplikacjach wielowątkowych.
  • srand(): Umożliwia inicjalizację generatora liczb losowych, co jest użyteczne do uzyskiwania różnych sekwencji liczb losowych.
Przykład użycia rand() i srand():
#include <iostream>
#include <cstdlib> // Dla rand() i srand()
#include <ctime>   // Dla time()

int main() {
    std::srand(std::time(nullptr)); // Inicjalizacja generatora za pomocą bieżącego czasu

    for(int n = 0; n < 10; ++n) {
        std::cout << std::rand() % 100 + 1 << ' '; // Generowanie liczb z zakresu 1-100
    }
    std::cout << '\n';

    return 0;
}
Porównanie <random> i rand()/srand()
  • Dokładność i kontrola: <random> oferuje znacznie większą kontrolę nad procesem generowania liczb losowych, w tym wybór generatora i rozkładu. rand() oferuje prostsze, lecz mniej kontrolowane podejście.
  • Wielowątkowość: Użycie rand() w aplikacjach wielowątkowych może prowadzić do problemów z powodu globalnego stanu generatora. <random> pozwala na tworzenie własnych instancji generatorów, co jest bezpieczniejsze w kontekście wielowątkowym.
  • Portowalność: Kod wykorzystujący <random> jest bardziej przenośny między różnymi kompilatorami i platformami w kontekście zachowania właściwości losowych sekwencji.
Podsumowanie
Dla nowoczesnego C++, zaleca się użycie biblioteki <random> do generowania liczb losowych ze względu na jej elastyczność, dokładność i bezpieczeństwo. Starsze metody z rand() nadal mogą być używane w prostych scenariuszach lub dla zachowania kompatybilności z istniejącym kodem, ale mają ograniczenia, które <random> skutecznie rozwiązuje.