Прежде всего, презентация, которую вы связали, говорит только о случайных числах в целях безопасности. Так что он не утверждает, что Random
это плохо для целей безопасности.
Но я утверждаю, что это так. Реализация .net 4 Random
имеет несколько недостатков. Я рекомендую использовать его только в том случае, если вам не важно качество случайных чисел. Я рекомендую использовать лучшие сторонние реализации.
Недостаток 1: посев
Конструктор по умолчанию содержит текущее время. Таким образом, все экземпляры, Random
созданные с помощью конструктора по умолчанию за короткий промежуток времени (около 10 мс), возвращают одну и ту же последовательность. Это задокументировано и сделано «в проект». Это особенно раздражает, если вы хотите использовать многопоточность своего кода, поскольку вы не можете просто создать экземпляр Random
в начале выполнения каждого потока.
Обходной путь - проявлять особую осторожность при использовании конструктора по умолчанию и при необходимости вручную заполнять его.
Другая проблема здесь в том, что начальное пространство довольно мало (31 бит). Итак, если вы сгенерируете 50 тысяч экземпляров Random
с совершенно случайными начальными числами, вы, вероятно, получите одну последовательность случайных чисел дважды (из-за парадокса дня рождения ). Так что сделать ручной посев тоже непросто.
Недостаток 2: распределение случайных чисел, возвращаемых Next(int maxValue)
функцией, смещено.
Есть параметры, по которым Next(int maxValue)
явно не однородно. Например, если вы подсчитаете, r.Next(1431655765) % 2
вы получите 0
примерно 2/3 образцов. (Пример кода в конце ответа.)
Недостаток 3: NextBytes()
метод неэффективен.
Стоимость байта NextBytes()
примерно равна стоимости генерации полной целочисленной выборки Next()
. Исходя из этого, я подозреваю, что они действительно создают одну выборку на байт.
Лучшая реализация, использующая 3 байта из каждой выборки, ускорится NextBytes()
почти в 3 раза.
Благодаря этому недостатку Random.NextBytes()
он всего на 25% быстрее, чем System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
на моей машине (Win7, Core i3 2600MHz).
Я уверен, что если бы кто-нибудь проверил исходный / декомпилированный байтовый код, он обнаружил бы еще больше недостатков, чем я обнаружил с помощью своего анализа черного ящика.
Примеры кода
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);
}