Przede wszystkim prezentacja, którą łączysz, mówi tylko o liczbach losowych ze względów bezpieczeństwa. Więc nie twierdzi, że Random
jest zły ze względów niezwiązanych z bezpieczeństwem.
Ale twierdzę, że tak. Implementacja .net 4 Random
jest wadliwa z kilku powodów. Zalecam używanie go tylko wtedy, gdy nie dbasz o jakość swoich liczb losowych. Polecam używanie lepszych implementacji innych firm.
Wada 1: Rozstawienie
Domyślny konstruktor nasiona z bieżącym czasem. Tak więc wszystkie instancje Random
utworzone za pomocą domyślnego konstruktora w krótkim czasie (ok. 10 ms) zwracają tę samą sekwencję. Jest to udokumentowane i „zgodne z projektem”. Jest to szczególnie denerwujące, jeśli chcesz wielowątkowość swojego kodu, ponieważ nie możesz po prostu utworzyć wystąpienia Random
na początku wykonywania każdego wątku.
Aby obejść ten problem, należy zachować szczególną ostrożność podczas korzystania z domyślnego konstruktora i ręcznie, gdy jest to konieczne.
Innym problemem jest to, że przestrzeń nasion jest raczej mała (31 bitów). Więc jeśli wygenerujesz 50k instancji Random
z idealnie losowymi nasionami, prawdopodobnie otrzymasz dwukrotnie jedną sekwencję liczb losowych (ze względu na paradoks urodzin ). Tak więc ręczne wysiewanie również nie jest łatwe.
Wada 2: Rozkład liczb losowych zwracanych przez Next(int maxValue)
jest obciążony
Istnieją parametry, które Next(int maxValue)
wyraźnie nie są jednolite. Na przykład, jeśli obliczysz r.Next(1431655765) % 2
, otrzymasz 0
około 2/3 próbek. (Przykładowy kod na końcu odpowiedzi.)
Wada 3: NextBytes()
Metoda jest nieefektywna.
Koszt za bajt NextBytes()
jest mniej więcej tak duży, jak koszt wygenerowania pełnej próbki całkowitej za pomocą Next()
. Na tej podstawie podejrzewam, że rzeczywiście tworzą jedną próbkę na bajt.
Lepsza implementacja wykorzystująca 3 bajty z każdej próbki przyspieszyłaby NextBytes()
prawie 3-krotnie.
Dzięki temu wada Random.NextBytes()
jest tylko o około 25% szybsza niż System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
na moim komputerze (Win7, Core i3 2600MHz).
Jestem pewien, że gdyby ktoś sprawdził źródłowy / zdekompilowany kod bajtowy, znalazłby jeszcze więcej błędów niż ja podczas analizy czarnej skrzynki.
Próbki kodu
r.Next(0x55555555) % 2
jest mocno stronniczy:
Random r = new Random();
const int mod = 2;
int[] hist = new int[mod];
for(int i = 0; i < 10000000; i++)
{
int num = r.Next(0x55555555);
int num2 = num % 2;
hist[num2]++;
}
for(int i=0;i<mod;i++)
Console.WriteLine(hist[i]);
Występ:
byte[] bytes=new byte[8*1024];
var cr=new System.Security.Cryptography.RNGCryptoServiceProvider();
Random r=new Random();
for(int i=0;i<100000;i++)
{
r.NextBytes(bytes);
}
for(int i=0;i<100000;i++)
{
for(int j=0;j<bytes.Length;j++)
bytes[j]=(byte)r.Next();
}
for(int i=0;i<100000;i++)
{
for(int j=0;j+2<bytes.Length;j+=3)
{
int num=r.Next();
bytes[j+2]=(byte)(num>>16);
bytes[j+1]=(byte)(num>>8);
bytes[j]=(byte)num;
}
}
for(int i=0;i<100000;i++)
{
cr.GetBytes(bytes);
}