Najszybszy sposób na wygenerowanie losowej wartości logicznej

87

Jest więc kilka sposobów tworzenia losowej wartości bool w C #:

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

Czy naprawdę jest różnica? Jeśli tak, to który z nich ma lepszą wydajność? A może jest inny sposób, którego nie widziałem, który mógłby być jeszcze szybszy?

timedt
źródło
7
Czy to naprawdę wąskie gardło?
Brian Rasmussen
2
Bez faktycznego uruchamiania go (ponieważ każda metoda będzie absurdalnie szybka), przypuszczam, że użyję NextBytesdo wstępnego wypełnienia tablicy bajtów, użyj jej BitArraydo przekształcenia jej w kolekcję wartości logicznych i odzyskaj te wartości logiczne z a Queueaż do opróżnienia, a następnie powtórz proces. Dzięki tej metodzie randomizera używasz tylko raz, więc wszelkie narzuty, które tworzy, pojawiają się tylko wtedy, gdy uzupełniasz kolejkę. Może to być przydatne w przypadku bezpiecznego generatora liczb losowych, a nie zwykłej Randomklasy.
Joe Enos
2
@JoeEnos MS zepsuł implementację NextBytes, więc jest zaskakująco wolny
CodesInChaos
1
@CodesInChaos Wow, to interesujące - po prostu sprawdziłem to w deasemblerze, aby zobaczyć, o czym mówisz: buffer[i] = (byte)(this.InternalSample() % 256);- Zakładam, że o tym mówisz, że mogli wziąć tę losową liczbę całkowitą i podzielić ją na 3 bajty , zapełniając tablicę bajtów około 1/3 pracy. Zastanawiam się, czy był ku temu powód, czy tylko niedopatrzenie ze strony deweloperów.
Joe Enos

Odpowiedzi:

72

Pierwsza opcja - rand.Next(2)Wykonuje za kulisami następujący kod:

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

a dla drugiej opcji - rand.NextDouble():

return this.Sample();

Ponieważ pierwsza opcja obejmuje maxValuewalidację, mnożenie i rzutowanie, druga opcja jest prawdopodobnie szybsza .

Aviran Cohen
źródło
Dziękuję, to wszystko, co chciałem wiedzieć.
timedt
5
Moje własne czasy mówią, że druga opcja jest około 5% szybsza niż pierwsza.
ClickRick
62

Małe rozszerzenie dla drugiej opcji :

Według MSDN

public virtual double NextDouble()

zwroty

Liczba zmiennoprzecinkowa podwójnej precyzji większa lub równa 0,0 i mniejsza niż 1,0.

Więc jeśli chcesz równomiernie rozłożyć losowy bool, powinieneś użyć >= 0.5

rand.NextDouble() >= 0.5

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

DaRich
źródło
10

Najszybszy. Wywołanie metody Random.Nextma mniejszy narzut. Poniższa metoda rozszerzenia działa o 20% szybciej niż Random.NextDouble() > 0.5io 35% szybciej niż 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]
}

Szybciej niż najszybciej. Możliwe jest Randomjeszcze szybsze generowanie losowych wartości logicznych z klasą za pomocą sztuczek. 31 znaczących bitów wygenerowanego intkodu można wykorzystać do 31 kolejnych produkcji logicznych. Poniższa implementacja jest o 40% szybsza niż wcześniej deklarowana jako najszybsza.

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
źródło
Dzięki! Dla tych, którzy używają UnityEngine do pisania skryptów, upewnij się, że używasz System.Random, a nie UnityEngine.Random, ponieważ to nie to samo!
DonCarleone
6

Przeprowadziłem testy ze stoperem. 100 000 iteracji:

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

Procesory takie jak liczby całkowite, więc metoda Next (2) była szybsza. 3700 w porównaniu z 7500 ms, co jest dość znaczące. Ponadto: Myślę, że losowe liczby mogą być wąskim gardłem, stworzyłem około 50 każdej klatki w Unity, nawet z małą sceną, która wyraźnie spowolniła mój system, więc miałem również nadzieję, że znajdę metodę tworzenia losowego bool. Więc też próbowałem

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

ale wywołanie funkcji statycznej było jeszcze wolniejsze i wynosiło 9600 ms. Warto spróbować. W końcu pominąłem porównanie i utworzyłem tylko 100 000 losowych wartości, aby upewnić się, że porównanie int vs double nie wpłynęło na upływający czas, ale wynik był prawie taki sam.

Frederik Steinmetz
źródło
1
DateTime.Now jest notorycznie powolne. Najlepiej byłoby, gdyby rozwiązania zależne od sprzętu lub przynajmniej zależne od systemu operacyjnego były szybkie.
Do-do-new
Użyj DateTime.UtcNow, jest znacznie szybszy niż DateTime.Now.
Theodor Zoulias