Generowanie liczb losowych w C ++ 11: jak generować, jak to działa? [Zamknięte]

102

Niedawno natknąłem się na nowy sposób generowania liczb losowych w C ++ 11, ale nie mogłem przetrawić artykułów, które o nim czytałem (co to za silnik , termin matematyczny taki jak dystrybucja , „gdzie wszystkie uzyskane liczby całkowite są równie prawdopodobne ”).

Więc czy ktoś może wyjaśnić

  • czym oni są?
  • co to znaczy
  • jak wygenerować?
  • jak oni pracują?
  • itp

Możesz to nazwać w jednym FAQ dotyczącym generowania liczb losowych.

informatik01
źródło
6
Pytanie o RNG bez wiedzy, co to jest dystrybucja, jest jak pytanie o parsery wyrażeń, nie wiedząc, czym jest wyrażenie ... Biblioteka RNG w C ++ 11 jest skierowana do ludzi, którzy znają jakieś statystyki i mają większe potrzeby niż płaska dystrybucja generowana przez 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.
Matteo Italia
26
@Matteo: Prawie. Dziecko może pojąć koncepcję, że kostka tworzy liczby losowe, nie rozumiejąc, czym jest rozkład.
Benjamin Lindley
3
@Benjamin: i na tym kończy się jego zrozumienie, co jest dopiero pierwszym krokiem (silniki) i nawet nie rozumie, dlaczego ważne jest, aby generowały płaską dystrybucję. Cała reszta biblioteki pozostaje tajemnicą bez zrozumienia dystrybucji i innych koncepcji statystycznych.
Matteo Italia

Odpowiedzi:

142

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/urandomkaż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_typewpisać 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_tna x64, w końcu to powinno być stałe i można powiedzieć, MyRNG::result_type seed_vala tym samym sprawiają, że silnik bardzo łatwo wymienialne.
Kerrek SB
źródło
Po raz kolejny Kerrek bije mnie do ponczu z dużo lepszą odpowiedzią niż ta, nad którą pracowałem. +1
Justin ᚅᚔᚈᚄᚒᚔ
@Justin: Jestem pewien, że przegapiłem mnóstwo rzeczy, nie krępuj się dodawać kolejnych aspektów do tego tematu! :-)
Kerrek SB
13
Myślę, że std::random_devicewarto wspomnieć o części „Jakoś /dev/urandom
zaludnij
2
Przykład std::random_devicemożna znaleźć tutaj .
WKS
1
Kod w artykule w Wikipedii zawiera błędy. random i random2 są identyczne. Z komentarzy we fragmencie kodu jasno wynika, że ​​autor nie rozumie, jak używać funkcji w <random>.
user515430,
3

Generator liczb losowych to równanie, które po podaniu liczby da nową liczbę. Zazwyczaj podajesz pierwszą liczbę lub jest ona pobierana z czegoś w rodzaju czasu systemowego.

Za każdym razem, gdy poprosisz o nową liczbę, używa poprzedniej liczby do wykonania równania.

Generator liczb losowych nie jest uważany za bardzo dobry, jeśli ma tendencję do generowania tej samej liczby częściej niż inne liczby. tj. jeśli chcesz losową liczbę od 1 do 5 i masz następujący rozkład liczb:

  • 1: 1%
  • 2: 80%
  • 3: 5%
  • 4: 5%
  • 5: 9%

2 jest generowany znacznie częściej niż jakakolwiek inna liczba, więc jest bardziej prawdopodobne, że zostanie wygenerowany niż inne liczby. Gdyby wszystkie liczby były takie same, miałbyś 20% szans na otrzymanie każdej liczby za każdym razem. Mówiąc inaczej, powyższy rozkład jest bardzo nierównomierny, ponieważ preferowane jest 2. Rozkład ze wszystkimi 20% byłby równy.

Zwykle, jeśli chcesz uzyskać prawdziwą liczbę losową, powinieneś pobrać dane z czegoś takiego jak pogoda lub inne naturalne źródło, a nie z generatora liczb losowych.

N_A
źródło
8
Większość generatorów liczb losowych generuje dobre, równe rozkłady. Po prostu nie są przypadkowe; problem polega na tym, że są one obliczane, a zatem możesz odgadnąć następną liczbę, mając wystarczającą liczbę w sekwencji (to czyni je szkodliwymi dla bezpieczeństwa tam, gdzie wymagane są liczby losowe). W przypadku gier i rzeczy powinno być dobrze.
Martin York
5
Jestem prawie pewien, że OP prosi o konkretne informacje o udogodnieniach podanych w nagłówku C ++ <losowo>. Ta odpowiedź nie dotyczy nawet programowania, nie mówiąc już o C ++.
Benjamin Lindley
1
@Martin: Bezpieczeństwo niekoniecznie wymaga źródła prawdziwie losowych liczb. AES w trybie licznika (na przykład) może działać całkiem nieźle, mimo że jest deterministyczny. Wymaga rozsądnej entropii w kluczu, ale nie prawdziwej losowości.
Jerry Coffin
@Benjamin Lindley: Nevermind. Po prostu ponownie przeczytałem i zdałem sobie sprawę, że się myliłem.
N_A