まず、リンクしたプレゼンテーションでは、セキュリティ上の理由から乱数についてのみ説明しています。したがって、Random
セキュリティ以外の目的で悪いとは主張していません。
しかし、私はそうだと主張します。の.net4実装にRandom
は、いくつかの点で欠陥があります。乱数の品質を気にしない場合にのみ使用することをお勧めします。より良いサードパーティの実装を使用することをお勧めします。
欠陥1:播種
デフォルトのコンストラクターは、現在の時刻をシードします。したがってRandom
、短い時間枠(約10ミリ秒)内にデフォルトのコンストラクターで作成されたすべてのインスタンスは、同じシーケンスを返します。これは文書化されており、「設計による」ものです。これは、コードをマルチスレッド化する場合に特に厄介ですRandom
。各スレッドの実行の開始時にのインスタンスを単純に作成することはできないためです。
回避策は、デフォルトコンストラクターを使用する場合は特に注意し、必要に応じて手動でシードすることです。
ここでのもう1つの問題は、シードスペースがかなり小さい(31ビット)ことです。したがって、Random
完全にランダムなシードを使用しての50kインスタンスを生成する場合、おそらく1つの乱数シーケンスを2回取得します(誕生日のパラドックスのため)。したがって、手動でシードすることも簡単ではありません。
欠陥2:によって返される乱数の分布にNext(int maxValue)
偏りがある
対象となるパラメータがあります Next(int maxValue)
明らかに均一はない。たとえば、計算r.Next(1431655765) % 2
すると0
、サンプルの約2/3が得られます。(回答の最後にあるサンプルコード。)
欠陥3:このNextBytes()
方法は非効率的です。
のバイトあたりのコストはNextBytes()
、で完全な整数のサンプルを生成するコストとほぼ同じNext()
です。このことから、実際にバイトごとに1つのサンプルが作成されていると思います。
各サンプルから3バイトを使用するより良い実装ではNextBytes()
、ほぼ3倍高速化されます。
この欠陥のおかげで Random.NextBytes()
System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
で、私のマシン(Win7、Core i3 2600MHz)よりも約25%高速です。
誰かがソース/逆コンパイルされたバイトコードを調べた場合、ブラックボックス分析で見つけたよりもさらに多くの欠陥が見つかると確信しています。
コードサンプル
r.Next(0x55555555) % 2
強く偏っている:
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]);
パフォーマンス:
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);
}