Найшвидший спосіб генерування випадкового логічного значення


87

Отже, існує кілька способів створити випадковий bool в C #:

  • Використання Random.Next (): rand.Next(2) == 0
  • Використання Random.NextDouble (): rand.NextDouble() > 0.5

Чи справді є різниця? Якщо так, то який із них насправді кращий? Або є інший спосіб, якого я не бачив, це може бути навіть швидше?



7
Це справді вузьке місце?
Брайан Расмуссен,

2
Фактично не запускаючи його (оскільки будь-який метод буде смішно швидким), я гадаю, було б використовувати NextBytesдля попереднього заповнення байтового масиву, BitArrayперетворити його на колекцію логічних типів і отримати ці логічні значення з, Queueпоки він не спорожниться, а потім повторити процес. За допомогою цього методу ви використовуєте рандомизатор лише один раз, тому будь-які накладні витрати, які він створює, трапляються лише тоді, коли ви поповнюєте чергу. Це може бути корисно при роботі з захищеним генератором випадкових чисел, а не зі звичайним Randomкласом.
Joe Enos,

2
@JoeEnos MS зіпсував реалізацію NextBytes, тому це напрочуд повільно
CodesInChaos

1
@CodesInChaos Wow, це цікаво - я просто розглянув це в розбірнику, щоб побачити, про що ви маєте на увазі: buffer[i] = (byte)(this.InternalSample() % 256);- Я припускаю, що це те, про що ви говорите, що вони могли взяти це випадкове ціле число і розділити його на 3 байти , заповнюючи масив байтів приблизно 1/3 роботи. Цікаво, чи була для цього причина, чи це був просто нагляд з боку розробників.
Joe Enos

Відповіді:


72

Перший варіант - rand.Next(2)виконує за лаштунками наступний код:

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

а для другого варіанту - rand.NextDouble():

return this.Sample();

Оскільки перший варіант містить maxValueперевірку, множення та кастинг, другий варіант, швидше за все, швидший .


Дякую, це все, що я хотів знати.
timedt

5
Мої власні терміни кажуть, що другий варіант приблизно на 5% швидший за перший.
ClickRick

60

Невелике вдосконалення для другого варіанту :

За даними MSDN

public virtual double NextDouble()

повертається

Число з плаваючою точкою з подвійною точністю більше або дорівнює 0,0, і менше 1,0.

Отже, якщо ви хочете рівномірно розподілений випадковий бульок, вам слід скористатися >= 0.5

rand.NextDouble() >= 0.5

Діапазон 1: [0,0 ... 0,5 [
Діапазон 2: [0,5 ... 1,0 [
| Діапазон 1 | = | Діапазон 2 |


9

Найшвидший. Виклик методу Random.Nextмає менші накладні витрати. Наведений нижче метод розширення працює на 20% швидше Random.NextDouble() > 0.5і на 35% швидше, ніж 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]
}

Швидше, ніж найшвидший. Можна генерувати випадкові булеві значення з Randomкласом ще швидше, використовуючи трюки. 31 значущий біт згенерованого intможна використовувати для 31 подальшого булевого виробництва. Реалізація нижче на 40% швидша, ніж раніше заявлена ​​як найшвидша.

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;
    }
}

Дякую! Для тих, хто використовує UnityEngine для написання скриптів, обов’язково використовуйте System.Random, а не UnityEngine.Random, оскільки це не одне і те ж!
DonCarleone

6

Я провів тести з секундоміром. 100 000 ітерацій:

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

Процесори люблять цілі числа, тому метод Next (2) був швидшим. 3700 проти 7500 мс, що є досить значним. Крім того: я думаю, що випадкові числа можуть бути вузьким місцем, я створив близько 50 кожного кадру в Unity, навіть з крихітною сценою, яка помітно сповільнила мою систему, тому я також сподівався знайти метод для створення випадкового булу. Тож я теж спробував

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

але виклик статичної функції був ще повільнішим - 9600 мс. Варто пострілу. Нарешті, я пропустив порівняння і створив лише 100 000 випадкових значень, щоб переконатися, що порівняння int та double не впливає на минулий час, але результат майже однаковий.


1
DateTime.Now, як відомо, повільно. В ідеалі для швидкого використання слід використовувати апаратно-залежні рішення або, принаймні, залежні від ОС.
Робіть-робіть нове

Використовуйте DateTime.UtcNow, це набагато швидше, ніж DateTime.Now.
Теодор Зуліас,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.