Przepraszam z góry, ponieważ jest to trochę trudne do zrozumienia ...
Po pierwsze, wiesz już, że java.util.Random
to wcale nie jest przypadkowe. Generuje sekwencje w całkowicie przewidywalny sposób z nasion. Masz całkowitą rację, ponieważ ponieważ ziarno ma tylko 64 bity, może wygenerować tylko 2 ^ 64 różnych sekwencji. Jeśli miałbyś w jakiś sposób wygenerować 64 prawdziwe losowe bity i użyć ich do wyboru ziarna, nie możesz użyć tego ziarna do losowego wyboru spośród wszystkich 52! możliwe sekwencje z jednakowym prawdopodobieństwem.
Jednak fakt ten nie ma znaczenia, o ile nie zamierzasz wygenerować więcej niż 2 ^ 64 sekwencji, o ile nie ma nic „specjalnego” ani „zauważalnie specjalnego” w sekwencjach 2 ^ 64, które może wygenerować .
Powiedzmy, że miałeś znacznie lepszy PRNG, który używał 1000-bitowych nasion. Wyobraź sobie, że masz dwa sposoby na zainicjowanie go - jeden sposób zainicjowałby go przy użyciu całego ziarna, a jeden sposób skróciłby ziarno do 64 bitów przed jego zainicjowaniem.
Jeśli nie wiesz, który inicjator jest który, to czy mógłbyś napisać test, aby je rozróżnić? Jeśli nie miałeś (aś) szczęścia na tyle, by skończyć inicjowanie złego z tymi samymi 64 bitami dwa razy, odpowiedź brzmi: nie. Nie można było rozróżnić dwóch inicjatorów bez szczegółowej wiedzy o słabościach w konkretnej implementacji PRNG.
Alternatywnie, wyobraź sobie, że Random
klasa miała tablicę 2 ^ 64 sekwencji, które zostały wybrane całkowicie i losowo w pewnym momencie w odległej przeszłości i że ziarno było tylko indeksem do tej tablicy.
Tak więc fakt, że Random
używa tylko 64 bitów dla jego nasienie, jest rzeczywiście nie koniecznie problem statystycznie, tak długo jak nie ma znaczącej szansa, że będzie korzystać z tego samego materiału siewnego dwukrotnie.
Oczywiście do celów kryptograficznych 64-bitowe ziarno nie jest wystarczające, ponieważ dwukrotne użycie systemu do tego samego zarodka jest wykonalne obliczeniowo.
EDYTOWAĆ:
Powinienem dodać, że pomimo tego, że wszystkie powyższe informacje są poprawne, faktyczna implementacja java.util.Random
nie jest niesamowita. Jeśli piszesz grę karcianą, może użyj MessageDigest
interfejsu API do wygenerowania skrótu SHA-256 "MyGameName"+System.currentTimeMillis()
i użyj tych bitów do przetasowania talii. Zgodnie z powyższym argumentem, tak długo, jak użytkownicy naprawdę nie uprawiają hazardu, nie musisz się martwić, że currentTimeMillis
powróci on długo. Jeśli użytkownicy są naprawdę hazardu, a następnie użyć SecureRandom
bez nasion.
Random
nigdy nie są prawdziwymi liczbami losowymi. Jest to PRNG, gdzie P oznacza „pseudo”. Dla prawdziwych liczb losowych potrzebujesz źródła losowości (takiego jak random.org).