Em primeiro lugar, a apresentação que você vinculou fala apenas sobre números aleatórios para fins de segurança. Portanto, ele não afirma Random
ser ruim para fins não relacionados à segurança.
Mas eu afirmo que sim. A implementação do .net 4 Random
apresenta falhas de várias maneiras. Eu recomendo usá-lo apenas se você não se importar com a qualidade de seus números aleatórios. Eu recomendo usar melhores implementações de terceiros.
Falha 1: a semeadura
O construtor padrão é propagado com a hora atual. Assim, todas as instâncias Random
criadas com o construtor padrão em um curto período de tempo (ca. 10ms) retornam a mesma sequência. Isso é documentado e "por design". Isso é particularmente irritante se você quiser multi-thread seu código, já que você não pode simplesmente criar uma instância de Random
no início de cada execução de thread.
A solução alternativa é ser extremamente cuidadoso ao usar o construtor padrão e propagar manualmente quando necessário.
Outro problema aqui é que o espaço da semente é bastante pequeno (31 bits). Portanto, se você gerar 50k instâncias de Random
com sementes perfeitamente aleatórias, provavelmente obterá uma sequência de números aleatórios duas vezes (devido ao paradoxo do aniversário ). Portanto, a semeadura manual também não é fácil de acertar.
Falha 2: A distribuição de números aleatórios retornados por Next(int maxValue)
é tendenciosa
Existem parâmetros para os quais Next(int maxValue)
claramente não são uniformes. Por exemplo, se você calcular r.Next(1431655765) % 2
, obterá 0
cerca de 2/3 das amostras. (Amostra de código no final da resposta.)
Falha 3: o NextBytes()
método é ineficiente.
O custo por byte de NextBytes()
é quase tão grande quanto o custo para gerar uma amostra inteira inteira com Next()
. A partir disso, suspeito que eles realmente criam uma amostra por byte.
Uma implementação melhor usando 3 bytes de cada amostra seria acelerada NextBytes()
em quase um fator 3.
Graças a esta falha, Random.NextBytes()
é apenas cerca de 25% mais rápido do que System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
na minha máquina (Win7, Core i3 2600MHz).
Tenho certeza de que se alguém inspecionasse o código de byte de origem / descompilado, encontraria ainda mais falhas do que eu encontrei com minha análise de caixa preta.
Amostras de código
r.Next(0x55555555) % 2
é fortemente tendencioso:
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]);
Atuação:
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);
}