Po co w ogóle używać klasy C # System.Random zamiast System.Security.Cryptography.RandomNumberGenerator?

85

Dlaczego ktokolwiek miałby w ogóle używać „standardowego” generatora liczb losowych z System.Random zamiast zawsze używać generatora liczb losowych zabezpieczonego kryptograficznie z System.Security.Cryptography.RandomNumberGenerator (lub jego podklas, ponieważ RandomNumberGenerator jest abstrakcyjny)?

Nate Lawson mówi nam w swojej prezentacji Google Tech Talk „ Crypto Strikes Back ” w minucie 13:11, aby nie używać „standardowych” generatorów liczb losowych z Pythona, Javy i C #, a zamiast tego używać wersji bezpiecznej kryptograficznie.

Znam różnicę między dwiema wersjami generatorów liczb losowych (patrz pytanie 101337 ).

Ale jakie jest uzasadnienie, aby nie zawsze używać bezpiecznego generatora liczb losowych? Dlaczego w ogóle używać System.Random? Może wydajność?

Lernkurve
źródło
7
Który wolisz wpisać?
Macha
13
Zbyt wiele osób poważnie traktuje to jako uzasadnienie tego, co robią (zwykle nie na głos). Kod czyta się więcej niż pisze, kogo obchodzą trywialne różnice w długości?
Mark Sowul
3
Ale w każdym razie dlaczego miałbyś używać kryptograficznych RNG, jeśli nie zajmujesz się kryptografią?
Mark Sowul
3
@Macha, do tego służą aliasy ->using R = System.Security.Cryptography.RandomNumberGenerator; R.Create();
cchamberlain

Odpowiedzi:

144

Szybkość i zamiar. Jeśli generujesz liczbę losową i nie potrzebujesz zabezpieczeń, po co używać powolnej funkcji kryptograficznej? Nie potrzebujesz zabezpieczeń, więc po co komuś sądzić, że numer może być użyty do czegoś bezpiecznego, gdy nie będzie?

Kevin LaBranche
źródło
30
Bardzo podoba mi się argument dotyczący intencji.
Lernkurve
12
Należy zauważyć, że Random.GetNext nie jest dobry w „rozprowadzaniu” losowych liczb w całym widmie, szczególnie w środowisku wątkowym. Natknąłem się na ten problem podczas pisania programu do testowania różnych rozwiązań problemu Rand7 z Rand5. W szybkim, wątkowym teście 100000 losowych liczb z zakresu od 0 do 10, 82470 wygenerowanych liczb to 0. Widziałem podobne rozbieżności w moich poprzednich testach. Losowość kryptografii jest bardzo równa w rozkładzie liczb. Myślę, że lekcja polega na tym, aby zawsze testować losowe dane, aby zobaczyć, czy są one „wystarczająco losowe” dla Twoich potrzeb.
Kristoffer L
35
@Kristoffer Myślę, że nadużyłeś Random. Niech zgadnę: utworzyłeś nową instancję Randomklasy dla każdej liczby, która, ponieważ jest zapoczątkowana przez zgrubny zegar, zostanie zapoczątkowana tą samą wartością w interwale około 1-16 ms.
CodesInChaos
15
@CodesInChaos: Poza tym istnieje warunek wyścigu, Randomktóry powoduje, że zwraca wszystkie 0, gdy ten sam obiekt jest używany z wielu wątków.
BlueRaja - Danny Pflughoeft,
3
@KristofferL: Patrz powyższy komentarz, zobacz także tę odpowiedź
BlueRaja - Danny Pflughoeft
65

Oprócz szybkości i bardziej użytecznego interfejsu ( NextDouble()itp.) Możliwe jest również utworzenie powtarzalnej losowej sekwencji przy użyciu stałej wartości ziarna. Przydaje się to między innymi podczas testowania.

Random gen1 = new Random();     // auto seeded by the clock
Random gen2 = new Random(0);    // Next(10) always yields 7,8,7,5,2,....
Henk Holterman
źródło
2
Jest jeszcze BitConverter.ToInt32 (wartość Byte [], int startIndex), który może być łatwiejszy do zrozumienia. ;)
sisve
7
Ian Bell i David Braben użyli generatora losowego w grze komputerowej Elite, aby stworzyć obszerną listę planet i ich atrybutów (rozmiar itp.), Z bardzo ograniczoną pamięcią. Zależy to również od generatora tworzącego deterministyczny wzorzec (z nasion) - którego Crypto oczywiście nie zapewnia (zgodnie z projektem). Więcej informacji o tym, jak to zrobili, jest tutaj: wiki.alioth.net/index.php / Random_number_generator oraz książka „Infinite Game Universe: Mathematical Techniques” ISBN: 1584500581 zawiera bardziej ogólną dyskusję na temat takich technik.
Daniel James Bryars
7
Należy pamiętać, że MSDN nie gwarantuje, że ta właściwość będzie obowiązywać we wszystkich wersjach platformy .NET:
Roman Starkov
2
@phoog „W rezultacie kod aplikacji nie powinien zakładać, że to samo ziarno spowoduje taką samą pseudolosową sekwencję w różnych wersjach platformy .NET Framework”. - Nie wiem, wydaje mi się to całkiem jasne. Nie zdziwiłbym się jednak, gdyby mimo tego ostrzeżenia nie mogli tego zmienić w praktyce bez przerywania istniejących programów.
Roman Starkov
2
@phoog: Mówisz jedną rzecz, a potem dokładnie odwrotnie. Bezpośrednio sobie zaprzeczasz.
Timwi,
53

Przede wszystkim prezentacja, którą łączysz, mówi tylko o liczbach losowych ze względów bezpieczeństwa. Więc nie twierdzi, że Randomjest zły ze względów niezwiązanych z bezpieczeństwem.

Ale twierdzę, że tak. Implementacja .net 4 Randomjest 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 Randomutworzone 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 Randomna 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 Randomz 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 0okoł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.GetBytesna 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();

// Random.NextBytes
for(int i=0;i<100000;i++)
{
    r.NextBytes(bytes);
}

//One sample per byte
for(int i=0;i<100000;i++)
{   
    for(int j=0;j<bytes.Length;j++)
      bytes[j]=(byte)r.Next();
}

//One sample per 3 bytes
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;
    }
    //Yes I know I'm not handling the last few bytes, but that won't have a noticeable impact on performance
}

//Crypto
for(int i=0;i<100000;i++)
{
    cr.GetBytes(bytes);
}
CodesInChaos
źródło
1
Ciekawe, mogę potwierdzić twoje odkrycie: na mojej maszynie Next (1431655765) daje również 2/3 przy dowolnym wysiewie. Jaka jest magia 1431655765? Jak doszedłeś do tego numeru?
citykid
1
@citykid Spójrz na liczbę w postaci szesnastkowej lub bitów. Jego magia wynika z wątpliwego sposobu, w jaki Randomużywa się do przekształcenia 31-bitowej liczby całkowitej w liczbę z określoną górną granicą. Zapomniałem szczegółów, ale to coś takiego randomValue * max / 2^{31}.
CodesInChaos
1431655765_10 = 10101010101010101010101010101_2
Tim S.
6
Hm. Więc jaką implementację Random dla C # polecasz?
Arsen Zahray
1
Święta krowa, niejednorodność rozkładu Next(), którą przedstawiłaś tutaj, jest dość spektakularnym błędem - i nadal występuje dzisiaj, 6 lat po tym, jak po raz pierwszy opisałeś swoje odkrycia. (Mówię „błąd”, a nie tylko „wada”, ponieważ dokumentacja twierdzi, że „liczby pseudolosowe są wybierane z równym prawdopodobieństwem z skończonego zbioru liczb” . Tak nie jest, a Twój kod to potwierdza.)
Mark Amery,
24

System.Random jest znacznie wydajniejszy, ponieważ nie generuje liczb losowych zabezpieczonych kryptograficznie.

Prosty test na mojej maszynie wypełniający bufor 4 bajtów losowymi danymi 1000000 razy zajmuje 49 ms dla Random, ale 2845 ms dla RNGCryptoServiceProvider. Zwróć uwagę, że jeśli zwiększysz rozmiar wypełnianego buforu, różnica zmniejszy się, ponieważ narzut dla RNGCryptoServiceProvider jest mniej istotny.

Michał
źródło
2
Dziękujemy za zademonstrowanie tego za pomocą rzeczywistego testu.
Lernkurve
3
Możesz pomyśleć, że jest to trudne, ale -1 w przypadku publikowania wyników testu porównawczego wydajności bez uwzględnienia kodu testu porównawczego. Nawet jeśli charakterystyka wydajności Randomi RNGCryptoServiceProvidernie zmieniła się w ciągu ostatnich 8 lat (o ile wiem, że mogły to zmienić), widziałem wystarczająco dużo całkowicie zepsutych testów porównawczych używanych w przepełnieniu stosu, aby nie ufać wynikom testu porównawczego, którego kod nie jest publicznie dostępny.
Mark Amery,
21

Najbardziej oczywiste powody zostały już wymienione, więc oto jeden z nich bardziej niejasny: kryptograficzne PRNG zazwyczaj muszą być nieustannie uzupełniane „prawdziwą” entropią. Tak więc, jeśli używasz CPRNG zbyt często, możesz wyczerpać pulę entropii systemu, która (w zależności od implementacji CPRNG) albo ją osłabi (umożliwiając napastnikowi to przewidzenie), albo zablokuje się podczas próby wypełnienia jego pulę entropii (stając się tym samym wektorem ataku dla ataku DoS).

Tak czy inaczej, Twoja aplikacja stała się teraz wektorem ataku dla innych, zupełnie niezwiązanych ze sobą aplikacji, które - w przeciwieństwie do twojej - w rzeczywistości niezwykle zależne od właściwości kryptograficznych CPRNG.

Przy okazji, jest to rzeczywisty problem świata rzeczywistego, który zaobserwowano na serwerach bezgłowych (które naturalnie mają raczej małe pule entropii, ponieważ brakuje im źródeł entropii, takich jak wejście myszy i klawiatury) z Linuksem, gdzie aplikacje nieprawidłowo używają /dev/randomCPRNG jądra dla wszystkich rodzajów liczb losowych, podczas gdy poprawnym zachowaniem byłoby odczytanie małej wartości początkowej z /dev/urandomi użycie jej do zapoczątkowania własnego PRNG.

Jörg W Mittag
źródło
Przeczytałem artykuł w Wikipedii i kilka innych źródeł internetowych na temat entropii i zubożenia entropii i nie do końca to rozumiem. Jak mogę wyczerpać pulę entropii, gdy generator liczb losowych jest zasilany czasem systemowym, liczbą wolnych bajtów itp.? Jak inni mogą używać go jako wektora ataku do przewidywania liczb losowych? Czy możesz podać prosty przykład? Być może tę dyskusję trzeba przenieść w tryb offline. en.wikipedia.org/wiki/Entropy_%28computing%29
Lernkurve
3
Czas systemowy nie jest źródłem entropii, ponieważ jest przewidywalny. Nie jestem pewien liczby wolnych bajtów, ale wątpię, czy jest to również wysokiej jakości źródło entropii. Wysyłając więcej żądań do serwera, atakujący może spowodować zmniejszenie liczby wolnych bajtów, czyniąc ją częściowo deterministyczną. Twoja aplikacja staje się wektorem ataku, ponieważ wyczerpując pulę entropii, zmusza inną, krytyczną dla bezpieczeństwa aplikację do używania mniej losowych liczb losowych - lub poczekaj, aż źródło entropii zostanie uzupełnione.
quant_dev
Rozumiem, że jeśli ktoś ma generator pseudolosowy zasilany np. 32-bitowym ziarnem, atak brutalnej siły często będzie dość łatwy; nawet 64-bitowe ziarno może być przedmiotem ataków urodzinowych. Jednak gdy ziarno stanie się znacznie większe, nie widzę ryzyka. Jeśli ktoś ma generator losowy, który dla każdego bajtu wyjściowego przyjmuje stan 128-bitowy przez algorytm szyfrowania blokowego, a następnie wyprowadza 8 dolnych bitów, w jaki sposób atakujący może nawet z gigantami kolejnych bajtów wyjściowych wywnioskować stan, brak słabych punktów w sam algorytm szyfrowania?
supercat
11

Jeśli programujesz grę karcianą lub loterię online, chciałbyś upewnić się, że sekwencja jest prawie niemożliwa do odgadnięcia. Jeśli jednak pokazujesz użytkownikom, powiedzmy, cytat z dnia, wydajność jest ważniejsza niż bezpieczeństwo.

Dan Diplo
źródło
9

Omówiono to obszernie, ale ostatecznie kwestia wydajności jest kwestią drugorzędną przy wyborze RPG. Istnieje szeroki wachlarz RNG, a konserwowany Lehmer LCG, z którego składa się większość systemowych RNG, nie jest najlepszy, ani nawet koniecznie najszybszy. Na starych, powolnych systemach był to doskonały kompromis. W dzisiejszych czasach ten kompromis rzadko jest naprawdę istotny. Ta rzecz utrzymuje się w dzisiejszych systemach głównie dlatego, że A) rzecz jest już zbudowana i nie ma prawdziwego powodu, aby `` odkrywać koło na nowo '' w tym przypadku, oraz B) ponieważ ogromna większość ludzi będzie jej używać 'wystarczająco dobry'.

Ostatecznie wybór RNG sprowadza się do stosunku ryzyka do korzyści. W niektórych aplikacjach, na przykład w grach wideo, nie ma żadnego ryzyka. Lehmer RNG jest więcej niż wystarczający i jest mały, zwięzły, szybki, dobrze zrozumiany i „w pudełku”.

Jeśli aplikacja jest na przykład grą w pokera online lub loterią, w której w pewnym momencie pojawiają się rzeczywiste nagrody, a do gry wchodzą prawdziwe pieniądze, „w pudełku” Lehmer nie jest już wystarczający. W wersji 32-bitowej ma tylko 2 ^ 32 możliwe prawidłowe stany, zanim zacznie w najlepszym przypadku cykliczne . W dzisiejszych czasach to otwarte drzwi do brutalnego ataku. W takim przypadku deweloper będzie chciał przejść do czegoś w rodzaju bardzo długiego okresu RNG niektórych gatunków i prawdopodobnie wysiać go od dostawcy o silnej kryptografii. Daje to dobry kompromis między szybkością a bezpieczeństwem. W takim przypadku osoba będzie szukała czegoś takiego jak Mersenne Twister lub pewnego rodzaju Multiple Recursive Generator .

Jeśli aplikacja jest czymś w rodzaju przekazywania dużych ilości informacji finansowych przez sieć, teraz istnieje ogromne ryzyko i znacznie przewyższa każdą możliwą nagrodę. Wciąż są samochody opancerzone, ponieważ czasami ciężko uzbrojeni ludzie są jedynym wystarczającym zabezpieczeniem, i uwierzcie mi, gdyby brygada ludzi do zadań specjalnych z czołgami, myśliwcami i helikopterami była finansowo opłacalna, byłaby to metoda z wyboru. W takim przypadku użycie silnego kryptograficznie RNG ma sens, ponieważ bez względu na poziom bezpieczeństwa, jaki możesz uzyskać, nie jest tak duży, jak chcesz. Więc weźmiesz tyle, ile możesz znaleźć, a koszt jest bardzo, bardzo odległym problemem na drugim miejscu, zarówno pod względem czasu, jak i pieniędzy. A jeśli to oznacza, że ​​wygenerowanie każdej losowej sekwencji na bardzo wydajnym komputerze zajmuje 3 sekundy, poczekasz 3 sekundy,


źródło
3
Myślę, że mylisz się co do swoich wielkości; przesyłanie danych finansowych musi być niezwykle szybkie; jeśli Twój algorytm handlowy może osiągnąć wynik o 0,1 ms szybciej niż konkurencja, lepiej trafisz w kolejkę poleceń kupna / sprzedaży / stop-loss / kwotowania. 3 sekundy to wieczność. Dlatego inwestorzy inwestują w niesamowicie dobre komputery. Zobacz poprzednią odpowiedź; Crypt.RNG zajmuje tylko 0,0028 ms na nowy numer; 0,0000028 sekund, więc tracisz 9 rzędów wielkości pod względem wymaganego przetwarzania, a także tego, jak ważna jest prędkość.
Henrik
4

Nie każdy potrzebuje zabezpieczonych kryptograficznie liczb losowych, a szybszy zwykły sposób może przynieść większe korzyści. Być może ważniejsze jest to, że możesz kontrolować sekwencję liczb System.Random.

W symulacji wykorzystującej liczby losowe, które możesz chcieć odtworzyć, ponownie uruchamiasz symulację z tym samym ziarnem. Może to być przydatne do śledzenia błędów, gdy chcesz również zregenerować dany błędny scenariusz - uruchamianie programu z dokładnie tą samą sekwencją liczb losowych, które spowodowały awarię programu.

nr
źródło
2

Jeśli nie potrzebuję zabezpieczeń, tj. Chcę tylko względnie nieokreślonej wartości, a nie takiej, która jest silna kryptograficznie, Random ma znacznie łatwiejszy w użyciu interfejs.

tvanfosson
źródło
2

Różne potrzeby wymagają różnych RNG. W przypadku kryptowalut chcesz, aby Twoje liczby losowe były jak najbardziej losowe. W przypadku symulacji Monte Carlo chcesz, aby równomiernie wypełniły przestrzeń i mogły rozpocząć RNG ze znanego stanu.

quant_dev
źródło
1
Gdyby tylko System.Random to zrobił ... no cóż.
user2864740
2

Random nie jest generatorem liczb losowych, jest deterministycznym generatorem sekwencji pseudolosowych, który bierze swoją nazwę ze względów historycznych.

Powodem jest użycie System.Randomtych właściwości, a mianowicie deterministycznej sekwencji, która gwarantuje uzyskanie tej samej sekwencji wyników po zainicjowaniu tego samego materiału siewnego.

Jeśli chcesz poprawić „losowość” bez poświęcania interfejsu, możesz odziedziczyć z System.Randomzastąpienia kilku metod.

Dlaczego miałbyś chcieć deterministycznej sekwencji

Jednym z powodów, dla których warto mieć raczej deterministyczną sekwencję niż prawdziwą losowość, jest to, że jest ona powtarzalna.

Na przykład, jeśli przeprowadzasz symulację numeryczną, możesz zainicjować sekwencję (prawdziwą) liczbą losową i zapisać, jaka liczba została użyta .

Następnie, jeśli chcesz powtórzyć dokładnie tę samą symulację, np. W celu debugowania, możesz to zrobić, zamiast tego inicjalizując sekwencję zapisaną wartością.

Dlaczego miałbyś chcieć tej konkretnej, niezbyt dobrej sekwencji

Jedynym powodem, jaki przychodzi mi do głowy, byłaby wsteczna kompatybilność z istniejącym kodem, który używa tej klasy.

Krótko mówiąc, jeśli chcesz poprawić sekwencję bez zmiany reszty kodu, śmiało.

Ben
źródło
1

Napisałem grę (Crystal Sliders on the iPhone: Here ), która wyświetlałaby „losową” serię klejnotów (obrazów) na mapie, a ty obracałeś mapę tak, jak chcesz, wybierałeś je i znikały. - Podobny do Bejeweled. Używałem Random () i został on obsadzony liczbą 100ns ticków od momentu uruchomienia telefonu, całkiem losowe ziarno.

Wydało mi się to niesamowite , że generował gry, które były prawie identyczne - z około 90 klejnotów, w 2 kolorach, dostałbym dwa DOKŁADNIE takie same, z wyjątkiem 1 do 3 klejnotów! Jeśli przerzucisz 90 monet i uzyskasz ten sam wzór z wyjątkiem 1-3 rzutów, jest to BARDZO mało prawdopodobne! Mam kilka zrzutów ekranu, które pokazują im to samo. Byłem zszokowany, jak zły był System.Random ()! Założyłem, że MUSZĘ napisać w swoim kodzie coś strasznie złego i źle go używać. Myliłem się jednak, to był generator.

Jako eksperyment - i ostateczne rozwiązanie, wróciłem do generatora liczb losowych, którego używam mniej więcej od 1985 roku - który jest OSTATECZNIE lepszy. Jest szybszy, ma okres 1,3 * 10 ^ 154 (2 ^ 521), zanim się powtórzy. Oryginalny algorytm został zapoczątkowany liczbą 16-bitową, ale zmieniłem ją na liczbę 32-bitową i poprawiłem początkowe wysiewanie.

Oryginał jest tutaj:

ftp://ftp.grnet.gr/pub/lang/algorithms/c/jpl-c/random.c

Przez lata rzuciłem każdy test liczb losowych, o którym mogłem pomyśleć, i przeszedł przez wszystkie. Nie spodziewam się, że ma jakąkolwiek wartość jako kryptograficzną, ale zwraca liczbę tak szybko, jak „return * p ++;” aż skończy się 521 bitów, a następnie wykonuje szybki proces na bitach, aby utworzyć nowe losowe.

Stworzyłem wrapper C # - nazwałam go JPLRandom () zaimplementowałem ten sam interfejs co Random () i zmieniłem wszystkie miejsca, w których wywołałem go w kodzie.

Różnica była OSTATECZNIE lepsza - OMG, byłem zdumiony - nie powinno być sposobu, bym mógł stwierdzić, patrząc tylko na ekrany z około 90 klejnotami w układzie, ale po tym wydałem awaryjną wersję mojej gry.

I nigdy więcej nie użyłbym System.Random () do niczego. Jestem W SZOKU, że ich wersja jest zdmuchnięta przez coś, co ma teraz 30 lat!

-Traderhut Games

Gry Traderhut
źródło
3
Moje pierwsze przypuszczenie jest takie, że Randomzbyt często odtwarzałeś . Powinien być tworzony tylko raz, gdy wielokrotnie wywołujesz Nexttę instancję. Randomjest zły, ale nie taki zły. Czy możesz opublikować przykładowy program wraz z parą nasion, które wykazują ten problem?
CodesInChaos
Kod utworzyłby Random () na początku każdego poziomu (ale był to poważny problem z poziomem 1 wyższym niż później). Kod wyglądał mniej więcej tak:
Traderhut Games
Rnd = new Random ((uint) GameSeed); NextGameSeed = Rnd.Next (2000000000); Każdy poziom wykorzystywał nowy losowy, który został utworzony z nowym nasieniem - Nasiono zostało zapisane dla każdego poziomu, więc mogłem odtworzyć mapę, a także potwierdzić sekwencję losowych nasion. To pozwala mi potwierdzić, że gra jest prawidłową serią map, które zostały rozwiązane i odtworzyć ją.
Traderhut Games
Początkowo Random został utworzony w oparciu o System.DateTime.Now.Ticks (lub 0), a następnie GameSeed został wybrany przy użyciu tego samego wywołania, co Rnd.Next () powyżej. Jeśli nie mogę tego zrobić, występuje poważny problem z inicjowaniem generatora liczb losowych.
Traderhut Games
to nie jest odpowiedź na pierwotne pytanie!
Mike Dinescu
-1

Ponieważ System.Random jest tu atakowany za jego "niepoprawność" i stronniczość, sprawdziłem się.

dystrybucja

Ten kod f # pokazuje, że zachowuje się naprawdę ładnie - na moim przeciętnym komputerze:

let r = System.Random()
Seq.init 1000000 (fun _ -> r.Next(0,10))
|> Seq.toList
|> Seq.groupBy id
|> Seq.map (fun (v,ls) -> v, ls |> Seq.length)
|> Seq.sortBy fst
|> Seq.iter (printfn "%A")

(0, 100208)
(1, 99744)
(2, 99929)
(3, 99827)
(4, 100273)
(5, 100280)
(6, 100041)
(7, 100001)
(8, 100175)
(9, 99522)    

Wersje frameworka, maszyna, system operacyjny - wszystko to może mieć znaczenie. Wprowadź kod w interaktywnym języku F # na swoim komputerze i spróbuj sam. Czytałem dla Cyrptography

let arr = [| 0uy |]
let rr = System. Security.Cryptography.RandomNumberGenerator.Create()
Seq.init 1000000 (fun _ -> rr.GetBytes(arr); arr.[0])
|> Seq.toList
|> Seq.groupBy id
|> Seq.map (fun (v,ls) -> v, ls |> Seq.length)
|> Seq.sortBy fst
|> Seq.take 10 // show first 10 bytes
|> Seq.iter (printfn "%A")

// distribution of first 10 bytes
(0uy, 3862)
(1uy, 3888)
(2uy, 3921)
(3uy, 3926)
(4uy, 3948)
(5uy, 3889)
(6uy, 3922)
(7uy, 3797)
(8uy, 3861)
(9uy, 3874)

występ

#time

let arr = [| 0uy |]

let r = System.Random()
Seq.init 1000000 (fun _ -> r.NextBytes(arr); arr.[0] |> int64) |> Seq.sum

Real: 00:00:00.204, CPU: 00:00:00.203, GC gen0: 45, gen1: 1, gen2: 1
val it : int64 = 127503467L

let rr = System. Security.Cryptography.RandomNumberGenerator.Create()
Seq.init 1000000 (fun _ -> rr.GetBytes(arr); arr.[0] |> int64) |> Seq.sum

Real: 00:00:00.365, CPU: 00:00:00.359, GC gen0: 44, gen1: 0, gen2: 0
val it : int64 = 127460809L

co sugeruje relację 1: 2 i nieco lepsze zachowanie pamięci w wersji kryptograficznej.

wniosek

Głównie ze względu na znacznie ładniejsze API, nieco za wydajność i całkiem dobrą dystrybucję preferowany jest System.Random. System.Random może również zmniejszyć zależności bibliotek, a jeśli framework zostanie przeniesiony, System.Random będzie prawdopodobnie dostępny przed wariantem Crypto.

citykid
źródło