Случайное число между 2 двойными числами


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
Это не работает, если минимальное значение равно double.MinValue, а максимальное значение равно double.MaxValue. Любые предложения о том, как справиться с этим вариантом использования?
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 случайных двойных double.MaxValueзначений между 0 и все значения были между 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

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

Игра с разными диапазонами показала мне, что линейная стратегия становится «вменяемой» с диапазоном от 0 до ushort.MaxValue«разумного» минимального значения 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/


Это лучше подходит для комментариев, так как даже не пытается ответить на фактический вопрос OP.
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.