Zunächst geht es in der von Ihnen verlinkten Präsentation aus Sicherheitsgründen nur um Zufallszahlen. Es wird also nicht behauptet, dass Random
es aus Sicherheitsgründen schlecht ist.
Aber ich behaupte es ist. Die .net 4-Implementierung von Random
ist in mehrfacher Hinsicht fehlerhaft. Ich empfehle, es nur zu verwenden, wenn Sie sich nicht für die Qualität Ihrer Zufallszahlen interessieren. Ich empfehle die Verwendung besserer Implementierungen von Drittanbietern.
Fehler 1: Die Aussaat
Der Standardkonstruktor legt die aktuelle Zeit fest. Somit geben alle Instanzen, Random
die mit dem Standardkonstruktor innerhalb eines kurzen Zeitrahmens (ca. 10 ms) erstellt wurden, dieselbe Sequenz zurück. Dies ist dokumentiert und "by-design". Dies ist besonders ärgerlich, wenn Sie Ihren Code mit mehreren Threads versehen möchten, da Sie nicht einfach Random
zu Beginn der Ausführung jedes Threads eine Instanz von erstellen können .
Die Problemumgehung ist besonders vorsichtig, wenn Sie den Standardkonstruktor verwenden und bei Bedarf manuell festlegen.
Ein weiteres Problem hierbei ist, dass der Startraum eher klein ist (31 Bit). Wenn Sie also 50.000 Instanzen Random
mit perfekt zufälligen Samen generieren, erhalten Sie wahrscheinlich zweimal eine Folge von Zufallszahlen (aufgrund des Geburtstagsparadoxons ). Manuelles Seeding ist also auch nicht einfach richtig zu machen.
Fehler 2: Die Verteilung der von zurückgegebenen Zufallszahlen Next(int maxValue)
ist voreingenommen
Es gibt Parameter, für die Next(int maxValue)
eindeutig keine Einheitlichkeit besteht. Wenn Sie zum Beispiel rechnen, erhalten r.Next(1431655765) % 2
Sie 0
ungefähr 2/3 der Stichproben. (Beispielcode am Ende der Antwort.)
Fehler 3: Die NextBytes()
Methode ist ineffizient.
Die Kosten pro Byte von NextBytes()
sind ungefähr so hoch wie die Kosten für die Erzeugung einer vollständigen Ganzzahl-Stichprobe mit Next()
. Daraus vermute ich, dass sie tatsächlich ein Sample pro Byte erstellen.
Eine bessere Implementierung mit 3 Bytes aus jedem Sample würde NextBytes()
sich um fast den Faktor 3 beschleunigen .
Dank dieses Fehlers Random.NextBytes()
ist nur etwa 25% schneller als System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
auf meinem Computer (Win7, Core i3 2600MHz).
Ich bin sicher, wenn jemand den Quell- / dekompilierten Bytecode inspiziert, findet er noch mehr Fehler als bei meiner Black-Box-Analyse.
Codebeispiele
r.Next(0x55555555) % 2
ist stark voreingenommen:
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]);
Performance:
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);
}