Funkcja JavaScript Math.random()
zwraca losową wartość z przedziału od 0 do 1, automatycznie wypełnianą na podstawie aktualnego czasu (podobnie jak ja wierzę w Javie). Jednak nie sądzę, aby można było ustawić sobie własne ziarno.
Jak mogę utworzyć generator liczb losowych, dla którego mogę podać własną wartość początkową, aby mógł generować powtarzalną sekwencję (pseudo) liczb losowych?
javascript
random
seed
scunliffe
źródło
źródło
Odpowiedzi:
Jedną z opcji jest http://davidbau.com/seedrandom, który jest rozwijalnym zamiennikiem Math.random () opartym na RC4 z ładnymi właściwościami.
źródło
Jeśli nie potrzebujesz możliwości seedowania, po prostu użyj
Math.random()
i zbuduj wokół niej funkcje pomocnicze (np.randRange(start, end)
).Nie jestem pewien, jakiego RNG używasz, ale najlepiej go poznać i udokumentować, abyś był świadomy jego cech i ograniczeń.
Jak powiedział Starkii, Mersenne Twister to dobry PRNG, ale nie jest łatwy do wdrożenia. Jeśli chcesz to zrobić sam, spróbuj zaimplementować LCG - jest to bardzo łatwe, ma przyzwoite cechy losowości (nie tak dobre jak Mersenne Twister) i możesz użyć niektórych popularnych stałych.
EDYCJA: rozważ świetne opcje w tej odpowiedzi dla krótkich implementacji RNG, w tym opcję LCG.
źródło
this.a * this.state
przekracza limit dokładnych liczb całkowitych w JavaScript, ponieważ prawdopodobnie spowoduje to liczbę większą niż 2 ^ 53. Rezultatem jest ograniczony zakres wydajności, a dla niektórych nasion prawdopodobnie bardzo krótki okres. Dalej, ogólnie rzecz biorąc, użycie potęgi dwa w celum
uzyskania dość oczywistych wzorców, kiedy i tak wydajesz operację modułu zamiast prostego obcięcia, nie ma powodu, aby nie używać liczby pierwszej.Jeśli chcesz mieć możliwość określenia ziarna, wystarczy zamienić wywołania
getSeconds()
igetMinutes()
. Możesz podać int i użyć połowy z tego mod 60 dla wartości sekund, a drugą połowę modulo 60, aby uzyskać drugą część.Biorąc to pod uwagę, ta metoda wygląda jak śmieci. Wykonanie prawidłowego generowania liczb losowych jest bardzo trudne. Oczywistym problemem jest to, że zarodek liczb losowych jest oparty na sekundach i minutach. Aby odgadnąć ziarno i odtworzyć strumień liczb losowych, wystarczy wypróbować 3600 różnych kombinacji sekund i minut. Oznacza to również, że istnieje tylko 3600 różnych możliwych nasion. Można to naprawić, ale od początku byłbym podejrzliwy wobec tego RNG.
Jeśli chcesz użyć lepszego RNG, wypróbuj Mersenne Twister . Jest to dobrze przetestowany i dość solidny RNG z ogromną orbitą i doskonałą wydajnością.
EDYCJA: Naprawdę powinienem mieć rację i odnosić się do tego jako do generatora liczb pseudolosowych lub PRNG.
źródło
Używam portu JavaScript Mersenne Twister: https://gist.github.com/300494 Pozwala to ręcznie ustawić ziarno. Ponadto, jak wspomniano w innych odpowiedziach, Mersenne Twister to naprawdę dobry PRNG.
źródło
Kod, który podałeś, wygląda trochę jak Lehmer RNG . W takim przypadku
2147483647
jest to największa 32-bitowa liczba całkowita2147483647
ze48271
znakiem , jest największą 32-bitową liczbą pierwszą i jest mnożnikiem pełnego okresu używanym do generowania liczb.Jeśli to prawda, można zmodyfikować
RandomNumberGenerator
, aby wziąć na dodatkowym parametremseed
, a następnie ustawićthis.seed
sięseed
; ale musiałbyś uważać, aby upewnić się, że ziarno spowoduje dobry rozkład liczb losowych (Lehmer może być taki dziwny) - ale większość nasion będzie w porządku.źródło
Poniżej znajduje się PRNG, który może być karmiony niestandardowym nasionem. Wywołanie
SeedRandom
zwróci losową funkcję generatora.SeedRandom
można wywołać bez argumentów w celu zapełnienia zwróconej funkcji losowej bieżącym czasem lub można ją wywołać z 1 lub 2 nieujemnymi liczbami całkowitymi jako argumentami, aby zapełnić ją tymi liczbami całkowitymi. Ze względu na dokładność punktu zmiennoprzecinkowego zaszczepienie tylko 1 wartością pozwoli na zainicjowanie generatora tylko w jednym z 2 ^ 53 różnych stanów.limit
Zwracana funkcja generatora losowego przyjmuje 1 argument będący liczbą całkowitą o nazwie , limit musi mieścić się w zakresie od 1 do 4294965886, funkcja zwróci liczbę z zakresu od 0 do limit-1.Przykładowe zastosowanie:
Ten generator ma następujące właściwości:
mod
wartości są liczbami pierwszymi, nie ma prostego wzoru na wyjściu, bez względu na wybraną granicę. W odróżnieniu od niektórych prostszych PRNG, które wykazują pewne dość systematyczne wzorce.źródło
for (var i = 0; i < 400; i++) { console.log("input: (" + i * 245 + ", " + i * 553 + ") | output: " + SeedRandom(i * 245, i * 553)(20)); }
Jeśli programujesz w Typescript, zaadaptowałem implementację Mersenne Twister, która została wprowadzona w odpowiedzi Christopha Henkelmanna do tego wątku, jako klasę maszynopisu:
możesz użyć go w następujący sposób:
sprawdź źródło, aby znaleźć więcej metod.
źródło
Zauważyłem, że ten kod się kręci i wydaje się, że działa dobrze, aby uzyskać liczbę losową, a następnie użyć ziarna, ale nie jestem do końca pewien, jak działa logika (np. Skąd pochodzą liczby 2345678901, 48271 i 2147483647).
źródło
RandomNumberGenerator
inextRandomNumber
faktycznie sięgają roku 1996. Przypuszczalnie jest to RNG Lehmer / LCG. Używa sprytnych działań matematycznych do wykonywania arytmetyki modulo na 32-bitowych liczbach całkowitych, które w przeciwnym razie byłyby zbyt małe, aby zawierały jakieś wartości pośrednie. Rzecz w tym, że JavaScript nie implementuje 32-bitowych liczb całkowitych, ale 64-bitowe liczby zmiennoprzecinkowe, a ponieważ dzielenie nie jest dzieleniem całkowitoliczbowym, tak jak ten kod zakłada, że wynik nie jest generatorem Lehmera. Daje pewne wyniki, które wydają się przypadkowe, ale gwarancje generatora Lehmera nie mają zastosowania.createRandomNumber
funkcja jest późniejszym dodatkiem, robi prawie wszystko źle, przede wszystkim tworzy instancję nowego RNG za każdym razem, gdy jest wywoływana, co oznacza, że wywołania w krótkich odstępach czasu będą używać tego samego float. W podanym kodzie jest prawie niemożliwe'a'
sparowanie z czymkolwiek innym niż'1'
i'red'
.OK, oto rozwiązanie, na które się zdecydowałem.
Najpierw utwórz wartość początkową za pomocą funkcji „newseed ()”. Następnie przekazujesz wartość ziarna do funkcji „srandom ()”. Na koniec funkcja „srandom ()” zwraca pseudolosową wartość z przedziału od 0 do 1.
Najważniejsze jest to, że wartość ziarna jest przechowywana w tablicy. Gdyby była to po prostu liczba całkowita lub zmiennoprzecinkowa, wartość byłaby nadpisywana przy każdym wywołaniu funkcji, ponieważ wartości liczb całkowitych, zmiennoprzecinkowych, łańcuchów itd. Są przechowywane bezpośrednio na stosie, a nie tylko wskaźniki, jak w przypadku tablic i inne obiekty. W ten sposób wartość ziarna może pozostać trwała.
Wreszcie, możliwe jest zdefiniowanie funkcji „srandom ()” w taki sposób, że jest to metoda obiektu „Math”, ale pozostawię to Tobie do rozgryzienia. ;)
Powodzenia!
JavaScript:
Lua 4 (moje osobiste środowisko docelowe):
źródło
seedobj[0] * seedobja
przekracza limit dokładnych liczb całkowitych w JavaScript, ponieważ prawdopodobnie spowoduje to liczbę większą niż 2 ^ 53. Rezultatem jest ograniczony zakres wydajności, a dla niektórych nasion prawdopodobnie bardzo krótki okres.