Czy jest możliwe zaszczepienie generatora liczb losowych (Math.random) w JavaScript?
javascript
random
płaczliwy
źródło
źródło
Odpowiedzi:
Nie, nie jest, ale dość łatwo jest napisać własny generator, a jeszcze lepiej użyć istniejącego. Sprawdź: to powiązane pytanie .
Zobacz także blog Davida Bau, aby uzyskać więcej informacji na temat seedowania .
źródło
UWAGA: Pomimo (a raczej z powodu) zwięzłości i pozornej elegancji, algorytm ten nie jest wysokiej jakości pod względem losowości. Poszukaj np. Tych wymienionych w tej odpowiedzi, aby uzyskać lepsze wyniki.
(Pierwotnie dostosowany do sprytnego pomysłu przedstawionego w komentarzu do innej odpowiedzi).
Możesz ustawić
seed
dowolną liczbę, po prostu unikaj zera (lub dowolnej wielokrotności Math.PI).Moim zdaniem elegancja tego rozwiązania wynika z braku jakichkolwiek „magicznych” liczb (oprócz 10000, co odpowiada mniej więcej minimalnej liczbie cyfr, którą należy wyrzucić, aby uniknąć nieparzystych wzorów - patrz wyniki o wartościach 10 , 100 , 1000 ). Brevity jest również miłe.
Jest nieco wolniejszy niż Math.random () (2 lub 3 razy), ale uważam, że jest tak szybki jak każde inne rozwiązanie napisane w JavaScript.
źródło
Zaimplementowałem wiele dobrych, krótkich i szybkich funkcji generatora liczb pseudolosowych (PRNG) w zwykłym JavaScript. Wszystkie z nich można wysiać i zapewnić dobrą jakość liczb.
Przede wszystkim zadbaj o prawidłową inicjalizację swoich PRNG. Większość poniższych generatorów nie ma wbudowanej procedury generowania nasion (dla uproszczenia), ale akceptuje jedną lub więcej 32-bitowych wartości jako stan początkowy PRNG. Podobne nasiona (np. Proste ziarno 1 i 2) mogą powodować korelacje w słabszych PRNG, w wyniku czego dane wyjściowe mają podobne właściwości (takie jak losowo generowane poziomy są podobne). Aby tego uniknąć, najlepszą praktyką jest inicjowanie PRNG z dobrze rozłożonym nasieniem.
Na szczęście funkcje skrótu są bardzo dobre w generowaniu zarodków PRNG z krótkich ciągów. Dobra funkcja skrótu generuje bardzo różne wyniki, nawet gdy dwa ciągi znaków są podobne. Oto przykład oparty na funkcji mieszania MurmurHash3:
Każde kolejne wywołanie funkcji powrotnej z
xmur3
produkcją nowego „Random” 32-bitową wartość skrótu, który będzie używany jako materiał siewny w PRNG. Oto jak możesz go użyć:Alternatywnie, po prostu wybierz dane zastępcze, którymi chcesz wypełnić ziarno, i przesuń generator kilka razy (12-20 iteracji), aby dokładnie wymieszać stan początkowy. Jest to często widoczne w referencyjnych implementacjach PRNG, ale ogranicza liczbę stanów początkowych.
Dane wyjściowe tych funkcji PRNG generują dodatnią liczbę 32-bitową (od 0 do 2 32 -1), która jest następnie przekształcana na liczbę zmiennoprzecinkową między 0-1 (0 włącznie, 1 wyłącznie) równoważną
Math.random()
, jeśli chcesz liczb losowych określonego zakresu, przeczytaj ten artykuł na MDN . Jeśli chcesz tylko surowych bitów, po prostu usuń ostatnią operację dzielenia.Należy również zwrócić uwagę na ograniczenia JS. Liczby mogą reprezentować tylko liczby całkowite do 53-bitowej rozdzielczości. W przypadku operacji bitowych liczba ta jest zmniejszana do 32. Utrudnia to implementację algorytmów napisanych w C lub C ++, które używają liczb 64-bitowych. Przeniesienie 64-bitowego kodu wymaga podkładek, które mogą drastycznie zmniejszyć wydajność. Ze względu na prostotę i wydajność rozważałem tylko algorytmy wykorzystujące matematykę 32-bitową, ponieważ jest ona bezpośrednio kompatybilna z JS.
sfc32 (prosty szybki licznik)
sfc32 jest częścią pakietu do testowania liczb losowych PractRand (który oczywiście przechodzi). sfc32 ma stan 128-bitowy i jest bardzo szybki w JS.
Morwa32
Mulberry32 jest prostym generatorem ze stanem 32-bitowym, ale jest niezwykle szybki i ma dobrą jakość (autor twierdzi, że przeszedł wszystkie testy pakietu testowego gjrand i ma pełny okres 32 , ale nie zweryfikowałem).
Poleciłbym to, jeśli potrzebujesz tylko prostego, ale przyzwoitego PRNG i nie potrzebujesz miliardów liczb losowych (patrz problem z urodzinami ).
xoshiro128 **
Od maja 2018 r. Xoshiro128 ** jest nowym członkiem rodziny Xorshift autorstwa Vigna / Blackman (który napisał również xoroshiro, który jest używany w Chrome). Jest to najszybszy generator oferujący stan 128-bitowy.
Autorzy twierdzą, że dobrze przechodzi testy losowości ( choć z zastrzeżeniami ). Inni badacze zauważyli, że niektóre testy w TestU01 nie udają się (szczególnie LinearComp i BinaryRank). W praktyce nie powinno to powodować problemów, gdy używane są zmiennoprzecinkowe (takie jak te implementacje), ale może powodować problemy, jeśli polegasz na surowych niskich bitach.
JSF (mały post Jenkinsa)
To jest JSF lub „smallprng” Boba Jenkinsa (2007), facet, który stworzył ISAAC i SpookyHash . To przechodzi testy PractRand i powinny być dość szybko, choć nie tak szybko jak SFC.
LCG (alias Lehmer / Park-Miller RNG lub MCG)
LCG jest niezwykle szybki i prosty, ale jakość jego losowości jest tak niska, że niewłaściwe użycie może faktycznie powodować błędy w twoim programie! Niemniej jednak jest znacznie lepszy niż niektóre odpowiedzi sugerujące użycie
Math.sin
lubMath.PI
! Jest to jednak jedna linijka, co jest miłe :).Ta implementacja nazywa się minimalnym standardowym RNG, jak zaproponował Park – Miller w 1988 i 1993 r. I zaimplementowano w C ++ 11 as
minstd_rand
. Należy pamiętać, że stan jest 31-bitowy (31 bitów daje 2 miliardy możliwych stanów, 32 bity daje dwa razy więcej). Jest to rodzaj PRNG, który inni próbują zastąpić!Będzie działał, ale nie użyłbym go, chyba że naprawdę potrzebujesz szybkości i nie zależy ci na jakości losowości (co w ogóle jest losowe?). Idealne do gry jam, demo lub coś takiego. LCG cierpią na korelacje nasion, więc najlepiej odrzucić pierwszy wynik LCG. A jeśli nalegasz na użycie LCG, dodanie wartości przyrostowej może poprawić wyniki, ale prawdopodobnie jest to ćwiczenie bezcelowe, gdy istnieją znacznie lepsze opcje.
Wydaje się, że istnieją inne mnożniki oferujące stan 32-bitowy (zwiększona przestrzeń stanu):
Te wartości LCG pochodzą z: P. L'Ecuyer: Tabela liniowych kongruencjalnych generatorów o różnych rozmiarach i dobrej strukturze sieci, 30 kwietnia 1997 r.
źródło
seed = (seed * 185852 + 1) % 34359738337
.Math.imul
pozwala na przepełnienie, tak jak w przypadku mnożenia w C na 32-bitowych liczbach całkowitych. Sugerujesz, że LCG wykorzystuje pełny zakres przestrzeni całkowitej JS, co jest zdecydowanie interesującym obszarem do zbadania. :)Nie, ale oto prosty generator pseudolosowy, implementacja Multiply-with-carry, którą zaadaptowałem z Wikipedii (od tego czasu został usunięty):
EDYCJA: naprawiono funkcję początkową poprzez jej zresetowanie m_z
EDIT2: Naprawiono poważne błędy implementacyjne
źródło
seed
Funkcja nie zresetować generatora liczb losowych, ponieważmz_z
zmienna ulega zmianie, gdyrandom()
jest tzw. Dlatego ustawmz_z = 987654321
(lub dowolną inną wartość) wseed
m_w
, niem_z
. 2) Obam_w
im_z
zmieniają się w oparciu o swoje poprzednie wartości, więc modyfikuje wynik.Algorytm Antti Sykäri jest ładny i krótki. Początkowo zrobiłem odmianę, która zastąpiła JavaScript Math.random JavaScript, gdy wywołujesz Math.seed (s), ale potem Jason skomentował, że zwrócenie funkcji byłoby lepsze:
Daje to kolejną funkcjonalność, której Javascript nie ma: wiele niezależnych generatorów losowych. Jest to szczególnie ważne, jeśli chcesz mieć jednocześnie wiele powtarzalnych symulacji.
źródło
Math.random
, które pozwoli ci mieć wiele niezależnych generatorów, prawda?Math.seed(42);
to robisz , resetuje funkcję, więc jeślivar random = Math.seed(42); random(); random();
dostaniesz0.70...
, to0.38...
. Jeśli zresetujesz go, dzwoniącvar random = Math.seed(42);
ponownie, to następnym razem, gdy zadzwoniszrandom()
, dostaniesz0.70...
ponownie, a następnym razem0.38...
znowu.random
zamiast nadpisania natywnej funkcji javascript. ZastąpienieMath.random
może spowodować, że kompilator JIST nie zoptymalizuje całego kodu.Zobacz prace Pierre'a L'Ecuyera sięgające końca lat 80. i początku lat 90. Są też inni. Samo tworzenie (pseudo) generatora liczb losowych, jeśli nie jesteś ekspertem, jest dość niebezpieczne, ponieważ istnieje wysokie prawdopodobieństwo, że wyniki nie będą statystycznie losowe lub będą miały krótki okres. Pierre (i inni) stworzyli kilka dobrych (pseudo) generatorów liczb losowych, które są łatwe do wdrożenia. Używam jednego z jego generatorów LFSR.
https://www.iro.umontreal.ca/~lecuyer/myftp/papers/handstat.pdf
Phil Troy
źródło
Łącząc niektóre z poprzednich odpowiedzi, jest to funkcja losowa, której szukasz:
źródło
Math.seed(0)()
zwraca0.2322845458984375
iMath.seed(1)()
zwraca0.23228873685002327
. Wydaje się, że zmiana obum_w
im_z
według nasion pomaga.var m_w = 987654321 + s; var m_z = 123456789 - s;
daje dobry rozkład pierwszych wartości dla różnych nasion.Napisanie własnego pseudolosowego generatora jest dość proste.
Sugestia Dave'a Scotese'a jest użyteczna, ale jak zauważyli inni, nie jest ona dość jednolicie rozłożona.
Nie dzieje się tak jednak z powodu całkowitej liczby argumentów grzechu. Jest tak po prostu z powodu zasięgu grzechu, który okazuje się być jednowymiarową projekcją koła. Jeśli zamiast tego przyjmiesz kąt koła, będzie on jednolity.
Więc zamiast sin (x) użyj arg (exp (i * x)) / (2 * PI).
Jeśli nie podoba ci się porządek liniowy, zmieszaj go nieco z xor. Faktyczny czynnik też nie ma tak wielkiego znaczenia.
Aby wygenerować n pseudolosowych liczb, można użyć kodu:
Należy również pamiętać, że nie można używać pseudolosowych sekwencji, gdy potrzebna jest prawdziwa entropia.
źródło
Wiele osób, które potrzebują w dzisiejszych czasach Javascript do generowania liczb losowych w JavaScript, korzysta z modułu seedrandom Davida Bau .
źródło
Math.random
nie, ale uruchomiona biblioteka rozwiązuje ten problem. Ma prawie wszystkie rozkłady, jakie możesz sobie wyobrazić, i obsługuje generowanie liczb losowych. Przykład:źródło
Napisałem funkcję, która zwraca rozstawioną liczbę losową, używa Math.sin, aby mieć długą liczbę losową i używa zarodka do wybierania liczb z tego.
Posługiwać się :
zwróci liczbę początkową; pierwszym parametrem jest dowolna wartość ciągu; twoje nasienie. drugim parametrem jest liczba cyfr, które powrócą.
źródło
Proste podejście do ustalonego materiału siewnego:
źródło
Dla liczby od 0 do 100.
źródło
Math.random
taki sposób, że za każdym razem, gdyMath.random
zostanie zaszczepiony tym samym ziarnem, wytworzy tę samą kolejną liczbę liczb losowych. To pytanie nie dotyczy, powiedzmy, faktycznego użycia / demonstracjiMath.random
.