Które z silników losowych liczb <losowych> powinno się faktycznie stosować w praktyce? std :: mt19937?

21

Załóżmy, że chcesz korzystać z narzędzi C ++ <random>w praktycznym programie (dla pewnej definicji „praktyczny” - ograniczenia tutaj są swego rodzaju częścią tego pytania). Masz mniej więcej taki kod:

int main(int argc, char **argv) {
    int seed = get_user_provided_seed_value(argc, argv);
    if (seed == 0) seed = std::random_device()();
    ENGINE g(seed);  // TODO: proper seeding?
    go_on_and_use(g);
}

Moje pytanie brzmi: do jakiego rodzaju należy używać ENGINE?

  • Zawsze mówiłem, std::mt19937bo szybko pisałem i rozpoznawałem nazwy. Ale w dzisiejszych czasach wydaje się, że wszyscy mówią, że Mersenne Twister jest bardzo ciężki i nieprzyjazny dla pamięci podręcznej i nie przechodzi nawet wszystkich testów statystycznych, które przeprowadzają inni.

  • Chciałbym powiedzieć, std::default_random_engineponieważ to oczywiste „domyślne”. Ale nie wiem, czy różni się w zależności od platformy i nie wiem, czy jest to statystycznie dobre.

  • Ponieważ każdy jest na platformie 64-bitowej te dni, powinniśmy być przynajmniej przy użyciu std::mt19937_64ponad std::mt19937?

  • Chciałbym powiedzieć pcg64lub xoroshiro128dlatego, że wydają się szanowani i lekcy, ale w ogóle ich nie ma <random>.

  • Nie wiem nic na temat minstd_rand, minstd_rand0, ranlux24, knuth_bna pewno muszą być dobre dla czegoś -, itd.?

Oczywiście istnieją tutaj pewne konkurencyjne ograniczenia.

  • Wytrzymałość silnika. ( <random>nie ma silnie kryptograficznie PRNG, ale niektóre ze standardowych są „słabsze” niż inne, prawda?)

  • sizeof silnik.

  • Szybkość jego operator().

  • Łatwość siewu. mt19937jest bardzo trudne do prawidłowego zaszczepienia, ponieważ ma tyle stanu do zainicjowania.

  • Przenośność między dostawcami bibliotek. Jeśli jeden sprzedawca foo_enginewytwarza inne numery od innego foo_engine, nie jest to dobre dla niektórych aplikacji. (Mam nadzieję, że nie wyklucza to niczego oprócz default_random_engine.)

Biorąc pod uwagę wszystkie te ograniczenia najlepiej, jak potrafisz, co powiedziałbyś, że jest najlepszą odpowiedzią na „pozostawanie w standardowej bibliotece”? Czy powinienem po prostu dalej używać std::mt19937, czy co?

Quuxplusone
źródło
2
Do ostatniego punktu wszystkie standardowe adaptery silnika są określone tak, aby zwracały określoną wartość przy konkretnym kolejnym wywołaniu domyślnie skonstruowanego, więc powinny być przenośne.
1201ProgramAlarm

Odpowiedzi:

15

C ++ Reference zawiera listę wszystkich losowych silników obecnie udostępnianych przez C ++. Jednak wybór silników pozostawia wiele do życzenia (np. Zobacz moją listę losowych generatorów wysokiej jakości ). Na przykład:

  • default_random_engine jest zdefiniowany w implementacji, więc nie wiadomo, czy silnik ma wady statystyczne, na których aplikacja może mieć znaczenie.
  • linear_congruential_enginewdraża liniowe generatory kongruencjalne. Mają jednak niską jakość, chyba że moduł jest pierwszy i bardzo duży (co najmniej 64 bity). Nie mogą też przyjąć większej liczby nasion niż ich moduł.
  • minstd_rand0i minstd_randprzyznać tylko około 2 ^ 31 nasion. knuth_bowija minstd_rand0i wykonuje losowanie Baysa-Durhama.
  • mt19937i mt19937_64mogliby przyznać o wiele więcej nasion, gdyby były lepiej zainicjalizowane (np. przez zainicjowanie std::seed_seqz wieloma wyjściami random_device, a nie tylko jednym), ale używają około 2500 bajtów stanu.
  • ranlux24i ranlux48używają około 577 bitów stanu, ale są one powolne (działają, utrzymując niektóre i odrzucając inne pseudolosowe dane wyjściowe).

Jednak C ++ ma również dwa silniki, które owijają inny silnik, aby potencjalnie poprawić jego właściwości losowości:

  • discard_block_engine odrzuca niektóre dane wyjściowe danego silnika losowego.
  • shuffle_order_engine implementuje losowanie Baysa-Durhama danego silnika losowego.

Na przykład, możliwe jest, powiedzmy, aby mieć Bays-Durham shuffle mt19937, ranlux24albo zwyczaju linear_congruential_enginez shuffle_order_engine. Być może owinięty silnik jest lepszej jakości niż oryginalny. Trudno jednak przewidzieć statystyczną jakość nowego silnika bez testowania .

Dlatego w oczekiwaniu na takie testy wydaje się, że mt19937jest to obecnie najbardziej praktyczny silnik w standardzie C ++. Mam jednak świadomość co najmniej jednej propozycji dodania kolejnego silnika liczb losowych do przyszłych wersji C ++ (patrz artykuł C ++ P2075 ).

Peter O.
źródło
1

Według C ++ Reference , default_random_engine:

Jest to wybór generatora biblioteki, który zapewnia co najmniej akceptowalne zachowanie silnika do stosunkowo swobodnego, niedoświadczonego i / lub lekkiego użytkowania.

Więc do lekkiego użytkowania nie musisz się o nic martwić, default_random_enginez nasionami Epoch Time (time(0))i to by było w porządku;)

Farbod Ahmadian
źródło
Uważam, że problemem jest przenośność. Domyślnym mechanizmem może być silnik, który działa dobrze, ale nie można go powielać na innej platformie.
bremen_matt
@bremen_matt Hmm ... Cóż, dlaczego musimy odtworzyć „losową” liczbę?
Farbod Ahmadian
2
Testowanie. Do celów testowych potrzebujesz odtwarzalnych danych wejściowych. Jednocześnie możesz chcieć lub potrzebujesz, aby te dane wejściowe były losowe. Na przykład większość algorytmów uczenia maszynowego zakłada, że ​​parametry są inicjowane losowo. Ransac, CNN, DNN, ... wiele algorytmów wymaga losowych parametrów.
bremen_matt