Випадкове число між двома подвійними числами


156

Чи можливо генерувати випадкове число між 2 парними?

Приклад:

public double GetRandomeNumber(double minimum, double maximum)
{
    return Random.NextDouble(minimum, maximum) 
}

Тоді я називаю це таким чином:

double result = GetRandomNumber(1.23, 5.34);

Будь-які думки були б вдячні.

Відповіді:


329

Так.

Random.NextDouble повертає удвічі між 0 і 1. Потім ви помножите це на діапазон, який вам потрібно перейти (різниця між максимумом і мінімумом), а потім додаєте його до бази (мінімум).

public double GetRandomNumber(double minimum, double maximum)
{ 
    Random random = new Random();
    return random.NextDouble() * (maximum - minimum) + minimum;
}

Реальний код повинен мати випадковий статичний член. Це дозволить заощадити витрати на створення генератора випадкових чисел і дозволить вам дзвонити в GetRandomNumber дуже часто. Оскільки ми ініціалізуємо новий RNG з кожним викликом, якщо ви дзвоните досить швидко, щоб системний час не змінювався між викликами, RNG отримає засідання з точно такою ж міткою часу та генерує той самий потік випадкових чисел.


33
Просто слідкуйте, якщо ви дзвоните GetRandomNumber () у циклі, оскільки він генеруватиме однакове значення знову і знову
Джон Раш

13
Може бути приємним способом продовження,public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5

5
Майте на увазі, що оскільки Random.NextDouble ніколи не повертає 1,0, ця функція також ніколи не буде дорівнює максимальній кількості.
Метью

6
Я пропоную поставити як насіння новий Random ((int) DateTime.Now.Ticks), це повинно зробити його "більш випадковим" навіть у швидких циклах.
Даніель Сковронський

5
Це не працює, якщо значення min подвійне. MinValue, а максимальне значення - подвійне. Будь-які пропозиції щодо розробки цього випадку використання?
Gene S

40

Johnny5 запропонував створити метод розширення. Ось більш повний приклад коду, який показує, як ви могли це зробити:

public static class RandomExtensions
{
    public static double NextDouble(
        this Random random,
        double minValue,
        double maxValue)
    {
        return random.NextDouble() * (maxValue - minValue) + minValue;
    }
}

Тепер ви можете назвати це так, ніби це метод на Randomкласі:

Random random = new Random();
double value = random.NextDouble(1.23, 5.34);

Зауважте, що вам не слід створювати багато нових Randomоб’єктів у циклі, оскільки це призведе до того, що ви отримаєте одне і те ж значення багато разів поспіль. Якщо вам потрібно багато випадкових чисел, то створіть один екземпляр Randomі повторно використовуйте його.


9

Будьте уважні: якщо ви генеруєте randomвнутрішню петлю, як, наприклад for(int i = 0; i < 10; i++), не кладітьnew Random() декларацію всередині циклу.

Від MSDN :

Генерація випадкових чисел починається із значення насіння. Якщо одне і те ж насіння використовується неодноразово, генерується однаковий ряд чисел. Один із способів отримання різних послідовностей - зробити значення насіння залежним від часу, тим самим створити різні серії з кожним новим екземпляром Random. За замовчуванням безпараметричний конструктор класу Random використовує системний тактовий годинник для створення свого початкового значення ...

Виходячи з цього факту, зробіть щось так:

var random = new Random();

for(int d = 0; d < 7; d++)
{
    // Actual BOE
    boes.Add(new LogBOEViewModel()
    {
        LogDate = criteriaDate,
        BOEActual = GetRandomDouble(random, 100, 1000),
        BOEForecast = GetRandomDouble(random, 100, 1000)
    });
}

double GetRandomDouble(Random random, double min, double max)
{
     return min + (random.NextDouble() * (max - min));
}

Таким чином ви маєте гарантію, що отримаєте різні подвійні значення.


8

Найпростіший підхід просто генерує випадкове число між 0 і різницею двох чисел. Потім до результату додайте менший з двох чисел.


3

Ви можете використовувати такий код:

public double getRandomNumber(double minimum, double maximum) {
    return minimum + randomizer.nextDouble() * (maximum - minimum);
}

2

Ви можете це зробити:

public class RandomNumbers : Random
{
    public RandomNumbers(int seed) : base(seed) { }

    public double NextDouble(double minimum, double maximum)
    {
        return base.NextDouble() * (maximum - minimum) + minimum;
    }
}

2

Я трохи запізнююся на партію, але мені потрібно було реалізувати загальне рішення, і виявилося, що жодне з рішень не може задовольнити мої потреби.

Прийняте рішення добре для невеликих діапазонів; однак, maximum - minimumможе бути нескінченним для великих діапазонів. Отже, виправлена ​​версія може бути цією версією:

public static double NextDoubleLinear(this Random random, double minValue, double maxValue)
{
    // TODO: some validation here...
    double sample = random.NextDouble();
    return (maxValue * sample) + (minValue * (1d - sample));
}

Це генерує випадкові числа добре навіть між double.MinValueі double.MaxValue. Але це вводить ще одну "проблему", яка добре представлена в цій публікації : якщо ми використовуємо такі великі діапазони, значення можуть здатися занадто "неприродними". Наприклад, після генерації 10000 випадкових подвоєнь між 0 іdouble.MaxValue всіма значеннями було між 2.9579E + 304 і 1.7976E + 308.

Тому я створив ще одну версію, яка генерує числа в логарифмічному масштабі:

public static double NextDoubleLogarithmic(this Random random, double minValue, double maxValue)
{
    // TODO: some validation here...
    bool posAndNeg = minValue < 0d && maxValue > 0d;
    double minAbs = Math.Min(Math.Abs(minValue), Math.Abs(maxValue));
    double maxAbs = Math.Max(Math.Abs(minValue), Math.Abs(maxValue));

    int sign;
    if (!posAndNeg)
        sign = minValue < 0d ? -1 : 1;
    else
    {
        // if both negative and positive results are expected we select the sign based on the size of the ranges
        double sample = random.NextDouble();
        var rate = minAbs / maxAbs;
        var absMinValue = Math.Abs(minValue);
        bool isNeg = absMinValue <= maxValue ? rate / 2d > sample : rate / 2d < sample;
        sign = isNeg ? -1 : 1;

        // now adjusting the limits for 0..[selected range]
        minAbs = 0d;
        maxAbs = isNeg ? absMinValue : Math.Abs(maxValue);
    }

    // Possible double exponents are -1022..1023 but we don't generate too small exponents for big ranges because
    // that would cause too many almost zero results, which are much smaller than the original NextDouble values.
    double minExponent = minAbs == 0d ? -16d : Math.Log(minAbs, 2d);
    double maxExponent = Math.Log(maxAbs, 2d);
    if (minExponent == maxExponent)
        return minValue;

    // We decrease exponents only if the given range is already small. Even lower than -1022 is no problem, the result may be 0
    if (maxExponent < minExponent)
        minExponent = maxExponent - 4;

    double result = sign * Math.Pow(2d, NextDoubleLinear(random, minExponent, maxExponent));

    // protecting ourselves against inaccurate calculations; however, in practice result is always in range.
    return result < minValue ? minValue : (result > maxValue ? maxValue : result);
}

Деякі тести:

Ось відсортовані результати генерування 10000 випадкових подвійних чисел між 0 та Double.MaxValueз обома стратегіями. Результати відображаються за допомогою логарифмічної шкали:

0..Double.MaxValue

Хоча лінійні випадкові значення здаються помилковими на перший погляд, статистика показує, що жодне з них не «краще», ніж інше: навіть лінійна стратегія має рівномірний розподіл, і середня різниця між значеннями майже однакова для обох стратегій .

Гра в різних діапазонах показала мені, що лінійна стратегія стає ushort.MaxValue"розумною " з діапазоном між 0 і "розумним" мінімальним значенням 10,78294704 (для ulongдіапазону мінімальне значення становило 3,03518E + 15 int;: 353341). Це однакові результати обох стратегій, що відображаються з різними масштабами:

0..UInt16.MaxValue


Редагувати:

Нещодавно я зробив свої бібліотеки з відкритим кодом, не соромтеся побачити RandomExtensions.NextDoubleметод з повною валідацією.


1

Що робити, якщо одне зі значень негативне? Не краща ідея:

double NextDouble(double min, double max)
{
       if (min >= max)
            throw new ArgumentOutOfRangeException();    
       return random.NextDouble() * (Math.Abs(max-min)) + min;
}

5
Я думаю, що Math.Abs()це зайве. Оскільки ви це переконалися min >= max, тоді max - minвсе одно має бути невід’ємне число.
Брайан Рейшл

1

Якщо вам потрібно випадкове число в діапазоні [ double.MinValue; double.MaxValue]

// Because of:
double.MaxValue - double.MinValue == double.PositiveInfinity

// This will be equals to NaN or PositiveInfinity
random.NextDouble() * (double.MaxValue - double.MinValue)

Використовуйте замість цього:

public static class RandomExtensions
{
    public static double NextDoubleInMinMaxRange(this Random random)
    {
        var bytes = new byte[sizeof(double)];
        var value = default(double);
        while (true)
        {
            random.NextBytes(bytes);
            value = BitConverter.ToDouble(bytes, 0);
            if (!double.IsNaN(value) && !double.IsInfinity(value))
                return value;
        }
    }
}

2
Хороші результати вказують, але цю пропозицію в нерівномірному розподілі , як у другому прикладі тут stackoverflow.com/a/3365388/3922292
Петро Фальковський

0

Про генерування того ж випадкового числа, якщо ви називаєте його в циклі, чудовим рішенням є оголошення нового об'єкта Random () поза циклом як глобальної змінної.

Зверніть увагу, що вам потрібно оголосити свій примірник класу Random за межами функції GetRandomInt, якщо ви збираєтеся це виконувати в циклі.

"Чому це?" Ви запитаєте.

Що ж, клас Random насправді генерує псевдовипадкові числа, при цьому "насінням" для рандомізатора є системний час. Якщо ваш цикл досить швидкий, час синхросигналу не буде відрізнятися від рандомизатора, і кожен новий екземпляр класу Random починається з того ж насіння і дасть вам те саме псевдо випадкове число.

Джерело тут: http://www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/


Це краще підходить як коментар, оскільки він навіть не намагається відповісти на власне питання ОП.
b1nary.atr0phy

0

Використовуйте статичну випадкову або цифри, як правило, повторюються в тісних / швидких циклах через системний годинник, що їх висіває.

public static class RandomNumbers
{
    private static Random random = new Random();
    //=-------------------------------------------------------------------
    // double between min and the max number
    public static double RandomDouble(int min, int max) 
    {
        return (random.NextDouble() * (max - min)) + min;
    }
    //=----------------------------------
    // double between 0 and the max number
    public static double RandomDouble(int max) 
    {
        return (random.NextDouble() * max);
    }
    //=-------------------------------------------------------------------
    // int between the min and the max number
    public static int RandomInt(int min, int max) 
    {   
        return random.Next(min, max + 1);
    }
    //=----------------------------------
    // int between 0 and the max number
    public static int RandomInt(int max) 
    {
        return random.Next(max + 1);
    }
    //=-------------------------------------------------------------------
 }

Дивіться також: https://docs.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.8


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