Pytanie jest o wiele za szerokie, aby dać pełną odpowiedź, ale pozwól mi wybrać kilka interesujących punktów:
Dlaczego „równie prawdopodobne”
Załóżmy, że masz prosty generator liczb losowych, który generuje liczby 0, 1, ..., 10 z jednakowym prawdopodobieństwem (pomyśl o tym jak o klasycznym rand()
). Teraz potrzebujesz losowej liczby z zakresu 0, 1, 2, z równym prawdopodobieństwem. Twoja odruchowa reakcja byłaby wzięta rand() % 3
. Ale czekaj, reszta 0 i 1 występuje częściej niż reszta 2, więc to nie jest poprawne!
Dlatego potrzebujemy odpowiednich rozkładów , które pobierają źródło jednakowych losowych liczb całkowitych i zamieniają je w nasz pożądany rozkład, jak Uniform[0,2]
w przykładzie. Najlepiej zostaw to w dobrej bibliotece!
Silniki
Zatem sercem całej losowości jest dobry generator liczb pseudolosowych, który generuje sekwencję liczb, które są równomiernie rozłożone w pewnym przedziale i które idealnie mają bardzo długi okres. Standardowa implementacja rand()
często nie jest najlepsza, dlatego warto mieć wybór. Liniowo-przystający i twister Mersenne to dwa dobre wybory (LG jest w rzeczywistości często używany również przez rand()
); znowu dobrze jest pozwolić bibliotece się tym zająć.
Jak to działa
Łatwe: najpierw skonfiguruj silnik i uruchom go. Ziarno w pełni określa całą sekwencję liczb „losowych”, więc a) użyj za /dev/urandom
każdym razem innego (np. Pobranego z ) i b) zapisz ziarno, jeśli chcesz odtworzyć sekwencję losowych wyborów.
#include <random>
typedef std::mt19937 MyRNG; // the Mersenne Twister with a popular choice of parameters
uint32_t seed_val; // populate somehow
MyRNG rng; // e.g. keep one global instance (per thread)
void initialize()
{
rng.seed(seed_val);
}
Teraz możemy tworzyć dystrybucje:
std::uniform_int_distribution<uint32_t> uint_dist; // by default range [0, MAX]
std::uniform_int_distribution<uint32_t> uint_dist10(0,10); // range [0,10]
std::normal_distribution<double> normal_dist(mean, stddeviation); // N(mean, stddeviation)
... i użyj silnika do tworzenia liczb losowych!
while (true)
{
std::cout << uint_dist(rng) << " "
<< uint_dist10(rng) << " "
<< normal_dist(rng) << std::endl;
}
Konkurencja
Jeszcze jeden ważny powód, aby preferować <random>
tradycyjnerand()
wątki, jest to, że teraz jest bardzo jasne i oczywiste, jak sprawić, by generowanie liczb losowych było bezpieczne: albo zapewnij każdemu wątkowi własny silnik lokalny dla wątku, zapoczątkowany na lokalnym źródle wątku lub zsynchronizuj dostęp do obiektu silnika.
Różne
- Ciekawy artykuł na temat TR1 random na codeguru.
- Wikipedia ma dobre podsumowanie (dzięki @Justin).
- W zasadzie każdy silnik powinien
result_type
wpisać f a , który jest prawidłowym typem całki do użycia dla nasienia. Chyba miał buggy realizację raz który zmusił mnie zmusić do materiału siewnego std::mt19937
, aby uint32_t
na x64, w końcu to powinno być stałe i można powiedzieć, MyRNG::result_type seed_val
a tym samym sprawiają, że silnik bardzo łatwo wymienialne.
rand
, powinieneś rzucić okiem na wikipedię, aby zapoznać się z podstawowymi pojęciami dotyczącymi statystyk i liczb losowych, w przeciwnym razie naprawdę trudno będzie wyjaśnić uzasadnienie<random>
i zastosowanie różnych elementów.