A maneira mais rápida de gerar um booleano aleatório

87

Portanto, há várias maneiras de criar um bool aleatório em C #:

  • Usando Random.Next (): rand.Next(2) == 0
  • Usando Random.NextDouble (): rand.NextDouble() > 0.5

Existe realmente alguma diferença? Em caso afirmativo, qual realmente tem o melhor desempenho? Ou há outra maneira que eu não vi, que pode ser ainda mais rápida?

cronometrado
fonte
7
Esse é realmente o gargalo?
Brian Rasmussen
2
Sem realmente executá-lo (porque qualquer método será ridiculamente rápido), meu palpite seria usar NextBytespara preencher previamente uma matriz de bytes, usá-la BitArraypara transformá-la em uma coleção de booleanos e recuperar esses booleanos de um Queueaté que seja esvaziado, depois repetir o processo. Com esse método, você está usando o randomizador apenas uma vez, portanto, qualquer sobrecarga que ele crie só acontece quando você reabastece a fila. Isso pode ser útil ao lidar com um gerador de números aleatórios seguro em vez de uma Randomclasse regular .
Joe Enos
2
@JoeEnos MS atrapalhou a implementação do NextBytes, por isso é surpreendentemente lento
CodesInChaos
1
@CodesInChaos Uau, isso é interessante - eu apenas pesquisei em um desmontador para ver ao que você estava se referindo: buffer[i] = (byte)(this.InternalSample() % 256);- Presumo que é disso que você está falando, que eles poderiam ter pegado aquele inteiro aleatório e dividido em 3 bytes , preenchendo a matriz de bytes com cerca de 1/3 do trabalho. Eu me pergunto se houve um motivo para isso ou se foi apenas um descuido dos desenvolvedores.
Joe Enos,

Respostas:

72

A primeira opção - rand.Next(2)executa nos bastidores o seguinte código:

if (maxValue < 0)
{
    throw new ArgumentOutOfRangeException("maxValue",
        Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", new object[] { "maxValue" }));
}
return (int) (this.Sample() * maxValue);

e para a segunda opção - rand.NextDouble():

return this.Sample();

Como a primeira opção contém maxValuevalidação, multiplicação e conversão, a segunda opção é provavelmente mais rápida .

Aviran Cohen
fonte
Obrigado, isso é tudo que eu queria saber.
cronometrado em
5
Meus próprios tempos dizem que a segunda opção é cerca de 5% mais rápida do que a primeira.
ClickRick
60

Pequeno aprimoramento para a segunda opção :

De acordo com MSDN

public virtual double NextDouble()

retorna

Um número de ponto flutuante de precisão dupla maior ou igual a 0,0 e menor que 1,0.

Então, se você quiser um bool aleatório distribuído uniformemente, você deve usar >= 0.5

rand.NextDouble() >= 0.5

Faixa 1: [0,0 ... 0,5 [
Faixa 2: [0,5 ... 1,0 [
| Faixa 1 | = | Faixa 2 |

DaRich
fonte
9

O mais rápido. Chamar o método Random.Nexttem menos sobrecarga. O método de extensão abaixo é executado 20% mais rápido do que Random.NextDouble() > 0.5e 35% mais rápido do que Random.Next(2) == 0.

public static bool NextBoolean(this Random random)
{
    return random.Next() > (Int32.MaxValue / 2);
    // Next() returns an int in the range [0..Int32.MaxValue]
}

Mais rápido do que o mais rápido. É possível gerar booleanos aleatórios com a Randomclasse ainda mais rápido, usando truques. Os 31 bits significativos de um gerado intpodem ser usados ​​para 31 produções booleanas subsequentes. A implementação abaixo é 40% mais rápida do que a anteriormente declarada como a mais rápida.

public class RandomEx : Random
{
    private uint _boolBits;

    public RandomEx() : base() { }
    public RandomEx(int seed) : base(seed) { }

    public bool NextBoolean()
    {
        _boolBits >>= 1;
        if (_boolBits <= 1) _boolBits = (uint)~this.Next();
        return (_boolBits & 1) == 0;
    }
}
Theodor Zoulias
fonte
Obrigado! Para aqueles que usam UnityEngine para escrever scripts, certifique-se de usar System.Random e não UnityEngine.Random, pois eles não são a mesma coisa!
DonCarleone
6

Fiz testes com cronômetro. 100.000 iterações:

System.Random rnd = new System.Random();
if (rnd.Next(2) == 0)
     trues++;

CPUs gostam de inteiros, então o método Next (2) foi mais rápido. 3.700 contra 7.500 ms, o que é bastante significativo. Além disso: eu acho que números aleatórios podem ser um gargalo, criei cerca de 50 cada quadro no Unity, mesmo com uma cena minúscula que tornou meu sistema visivelmente lento, então eu também esperava encontrar um método para criar um bool aleatório. Então eu também tentei

if (System.DateTime.Now.Millisecond % 2 == 0)
       trues++;

mas chamar uma função estática foi ainda mais lento com 9.600 ms. Vale a pena experimentar. Por fim, pulei a comparação e criei apenas 100.000 valores aleatórios, para ter certeza de que a comparação int vs. dupla não influenciava o tempo decorrido, mas o resultado era praticamente o mesmo.

Frederik Steinmetz
fonte
1
DateTime.Now é notoriamente lento. Idealmente, soluções dependentes de hardware ou pelo menos dependentes do sistema operacional devem ser usadas para torná-lo rápido.
Faça de novo
Use DateTime.UtcNow, é muito mais rápido do que DateTime.Now.
Theodor Zoulias