2013-04-15 13 views
8

Próbuję realizować następujące klasy:Z czego składa się losowa dystrybucja C++ 11?

typedef std::mt19937 Engine; 

class Interval 
{ 
public: 
    double upperBoundary; 
    double lowerBoundary; 
    double generateUniformRandomNumber(Engine& engine); 
}; 

chcę klasa do pracy w środowisku wielowątkowym. Każdy wątek będzie miał własną instancję obiektu Engine i przekaże Engine obiektom dowolnej klasy, która ma losowe zachowanie.

W celu wygenerowania liczb losowych jednostajnie na C++ 11 sposób, realizacja generateUniformRandomNumber musiałoby być coś takiego:

uniform_real_distribution<double> distribution_; // private member of Interval 

double Interval::generateUniformRandomNumber(Engine& engine) 
{ 
    return distribution_(engine); 
} 

Problemem jest to, że nie rozumiem, C++ 11 dystrybucje. Wiem, że silniki liczb losowych C++ 11 mogą być bardzo dużymi obiektami (kilka kilobajtów), ale co z dystrybucjami? Początkowo myślałem, że dystrybucje są po prostu prostymi funktorami, gdzie operator() jest funkcją pure const, ale wydaje się, że nie jest to ani pure ani const. Zgodnie z reference każda instancja dystrybucji ma funkcję składową reset(). Oznacza to, że ma potencjalnie duży wewnętrzny stan, a może pamięć podręczną.

Moje pytanie brzmi:

  1. Czy dystrybucje mają stan wewnętrzny? Jeśli tak, dlaczego? Czy standard mówi cokolwiek o wielkości tego stanu?

  2. Czy to dobry pomysł, aby wykonać wdrożenie tak jak ja? Czy istnieje lepszy sposób?

+3

Jako przykład zaimplementowałem [wersję beta] (https://gist.github.com/sftrabbit/5068941), której stan to właściwie dwie dystrybucje gamma. 'reset' nic nie robi, ponieważ jedynym wymaganiem jest, aby jakiekolwiek poniższe wartości były niezależne od poprzednich zastosowań silników. –

+0

@sftrabbit Tak więc jest 'reset()' na wypadek, gdyby ktoś dokonał dystrybucji, która kumuluje stan, gdy wywołasz 'operator()'? Na przykład każde kolejne wywołanie 'operator()' może dać ci zwiększający się zakres ... co również tłumaczy, dlaczego 'operator()' nie jest const, jak sądzę? – David

+0

@Dave Tak, nie ma powodu, aby dystrybucja nie mogła się zmienić w trakcie wywoływania 'operator()'. Oznacza to tylko, że wynikowe wartości zależą od poprzednich inwokacji. –

Odpowiedz

2

Spójrz na docs dla RandomNumberDistribution template policy ...

reset():

resetuje wewnętrzny stan obiektu dystrybucyjnego. Po wywołaniu tej funkcji następne wywołanie operatora() na obiekcie dystrybucyjnym nie będzie zależne od poprzednich wywołań operatora().

Oznacza to, że połączenia z numerem operator() mogą zmienić stan, który powoduje późniejsze połączenia z numerem operator(). To dlatego istnieje reset() i dlaczego operator() nie jest const.

uniform_real_distribution powinien być małym prostym funktorem, jak powiedziałeś. Najprawdopodobniej po prostu pomieściłby 2 Real, z których był zbudowany i nic więcej. I reset() nie powinien nic robić dla tego.

6

Rozkład może bardzo dobrze i zwykle będzie miał jakiś stan. Standard nie daje tutaj żadnych ograniczeń. Mogę wymyślić kilka przyczyn tego stanu:

  1. Rozkład liczb losowych bardzo często ma kilka parametrów do skonfigurowania. Na przykład rozkład normalny ma parametry średniej i wariancji. Są one częścią jego stanu, ponieważ muszą być utrzymywane pomiędzy inwokacjami.

  2. Dystrybucja jest realizowana pod względem innej dystrybucji. Można tak naprawdę uznać to za bardziej złożony parametr. Na przykład moja implementacja beta distribution utrzymuje dwa rozkłady gamma, z których każdy ma swoją własną konfigurację.

  3. Rozkład może się zmieniać z czasem. Nie ma nic do powiedzenia, że ​​powtórne wywołania dystrybucji muszą być niezależne. Tutaj pojawia się funkcja składowa reset. Większość dystrybucji ma niezależne wywołania do operator(), więc funkcja reset w rzeczywistości nic nie robi (jej definicja jest pusta). Jeśli jednak twoje połączenia są zależne, reset powinno przywrócić dystrybucję do stanu, w którym następne połączenie jest niezależne.

Twoje wdrożenie wydaje się być w porządku. A uniform_real_distribution<double> jest bardzo mało prawdopodobne, aby mieć o wiele więcej stanu niż parametry, które je konstruuje.

2

Tak, dystrybucje mogą mieć stany wewnętrzne. Zazwyczaj będą małe, ale jeśli masz wątpliwości co do rozmiaru, sprawdź to.

2

specyfikacji reset() stanowi:

Kolejne zastosowania D nie zależy od wartości przedstawionych przez dowolną silnika przed wywoływania reset.

To oznacza, że ​​normalnie wywołań operator() może zależeć od wartości wytwarzanych przez silniki wykorzystywane w uprzednim wywołaniu operator(). Oznacza to, że dystrybucja może buforować lub w inny sposób zależeć od poprzednich wyników silnika.

Na przykład dystrybucja Bernoulliego może potrzebować tylko jednego bitu, aby uzyskać wynik, podczas gdy dany silnik dostarcza 32 bity na raz, więc dystrybucja może buforować 32 bity i nie wywoływać żadnego silnika ponownie, dopóki nie zostaną wygenerowane 32 wartości. .

Kolejnym przykładem jest rozkład (nie pamiętam), w którym wspólny algorytm w naturalny sposób tworzy dwie wartości naraz, więc dystrybucja może zapisać drugą wartość dla następnego połączenia.

Tak, dystrybucje mogą mieć stan wewnętrzny. Standard nie określa wymagań co do wielkości tego stanu.

Jeśli pytasz, czy podzielenie dystrybucji między wątkami byłoby w porządku, nie, to nie jest dobry pomysł. Przede wszystkim jest to wyścig danych i powoduje niezdefiniowane zachowanie, chyba że dodasz synchronizację lub sprawisz, że stała dystrybucji (która, nawet jeśli możesz to zrobić przy implementacji standardowej biblioteki, nie jest przenośna). Po drugie, standard udziela gwarancji tylko wtedy, gdy używasz dystrybucji i silników w określony sposób, a podział dystrybucji między kilka silników nie jest właściwy. Jest mało prawdopodobne, że dzielenie się dystrybucją rzeczywiście spowoduje powstanie złych danych, ale IMO nadal jest dobrym pomysłem, aby tego nie robić. Zamiast tego każdy wątek może mieć zarówno własny silnik, jak i własną dystrybucję.