Número aleatorio entre 2 números dobles

156

¿Es posible generar un número aleatorio entre 2 dobles?

Ejemplo:

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

Luego lo llamo con lo siguiente:

double result = GetRandomNumber(1.23, 5.34);

Cualquier idea sería apreciada.

CodeLikeBeaker
fuente

Respuestas:

329

Si.

Random.NextDouble devuelve un doble entre 0 y 1. Luego lo multiplica por el rango que necesita ingresar (diferencia entre máximo y mínimo) y luego agrega eso a la base (mínimo).

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

El código real debe tener al azar ser un miembro estático. Esto ahorrará el costo de crear el generador de números aleatorios y le permitirá llamar a GetRandomNumber con mucha frecuencia. Como estamos inicializando un nuevo RNG con cada llamada, si llama lo suficientemente rápido como para que la hora del sistema no cambie entre llamadas, el RNG se sembrará con la misma marca de tiempo y generará la misma secuencia de números aleatorios.

Miguel
fuente
33
Solo tenga cuidado si llama a GetRandomNumber () en un bucle ya que generará el mismo valor una y otra vez
John Rasch
13
Podría ser un buen método de extensión,public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5
55
Tenga en cuenta que dado que Random.NextDouble nunca devuelve 1.0, esta función nunca será igual al número máximo.
Mateo
66
Sugiero poner como semilla un nuevo Random ((int) DateTime.Now.Ticks) esto debería hacerlo "más aleatorio" incluso en bucles rápidos.
Daniel Skowroński
55
Esto no funciona si el valor mínimo es double.MinValue y el valor máximo es double.MaxValue. ¿Alguna sugerencia sobre cómo manejar este caso de uso?
Gene S
40

Johnny5 sugirió crear un método de extensión. Aquí hay un ejemplo de código más completo que muestra cómo podría hacer esto:

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

Ahora puede llamarlo como si fuera un método en la Randomclase:

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

Tenga en cuenta que no debe crear muchos Randomobjetos nuevos en un bucle porque esto hará que sea probable que obtenga el mismo valor muchas veces seguidas. Si necesita muchos números aleatorios, cree una instancia Randomy reutilícela.

Mark Byers
fuente
9

Cuidado: si estás generando el randominterior de un ciclo como, por ejemplo for(int i = 0; i < 10; i++), no pongas elnew Random() declaración dentro del ciclo.

De MSDN :

La generación de números aleatorios comienza desde un valor semilla. Si se usa la misma semilla repetidamente, se genera la misma serie de números. Una forma de producir diferentes secuencias es hacer que el valor semilla dependa del tiempo, produciendo así una serie diferente con cada nueva instancia de Aleatorio. Por defecto, el constructor sin parámetros de la clase Random usa el reloj del sistema para generar su valor semilla ...

Entonces, en base a este hecho, haga algo como:

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

Al hacerlo, tiene la garantía de que obtendrá diferentes valores dobles.

Leniel Maccaferri
fuente
8

El enfoque más simple simplemente generaría un número aleatorio entre 0 y la diferencia de los dos números. Luego agregue el menor de los dos números al resultado.

Greg D
fuente
3

Podrías usar un código como este:

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

Podrías hacer esto:

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

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

Llegué un poco tarde a la fiesta, pero necesitaba implementar una solución general y resultó que ninguna de las soluciones puede satisfacer mis necesidades.

La solución aceptada es buena para rangos pequeños; sin embargo, maximum - minimumpuede ser infinito para grandes rangos. Entonces una versión corregida puede ser esta versión:

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

Esto genera números aleatorios muy bien incluso entre double.MinValuey double.MaxValue. Pero esto introduce otro "problema", que se presenta muy bien en esta publicación : si usamos rangos tan grandes, los valores pueden parecer demasiado "antinaturales". Por ejemplo, después de generar 10,000 dobles al azar entre 0 y double.MaxValuetodos los valores estaban entre 2.9579E + 304 y 1.7976E + 308.

Así que creé también otra versión, que genera números en una escala logarítmica:

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

Algunas pruebas:

Aquí están los resultados ordenados de generar 10,000 números dobles aleatorios entre 0 y Double.MaxValuecon ambas estrategias. Los resultados se muestran usando la escala logarítmica:

0..Double.MaxValue

Aunque los valores aleatorios lineales parecen estar equivocados a primera vista, las estadísticas muestran que ninguno de ellos es "mejor" que el otro: incluso la estrategia lineal tiene una distribución uniforme y la diferencia promedio entre los valores es prácticamente la misma con ambas estrategias. .

Jugar con diferentes rangos me mostró que la estrategia lineal llega a ser "sana" con un rango entre 0 y ushort.MaxValuecon un valor mínimo "razonable" de 10.78294704 (para el ulongrango, el valor mínimo era 3.03518E + 15 int;: 353341). Estos son los mismos resultados de ambas estrategias que se muestran con diferentes escalas:

0..UInt16.MaxValue


Editar:

Recientemente hice mis bibliotecas de código abierto, siéntase libre de ver el RandomExtensions.NextDoublemétodo con la validación completa.

György Kőszeg
fuente
1

¿Qué pasa si uno de los valores es negativo? No sería una mejor idea:

double NextDouble(double min, double max)
{
       if (min >= max)
            throw new ArgumentOutOfRangeException();    
       return random.NextDouble() * (Math.Abs(max-min)) + min;
}
Chris Susie
fuente
55
Creo que el Math.Abs()es redundante. Como se aseguró eso min >= max, max - mindebe ser un número no negativo de todos modos.
Brian Reischl
1

Si necesita un número aleatorio en el rango [ 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)

Use en su lugar:

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;
        }
    }
}
alex
fuente
2
Buen punto, pero esta propuesta da como resultado una distribución sesgada como en el segundo ejemplo aquí stackoverflow.com/a/3365388/3922292
Piotr Falkowski
0

Acerca de generar el mismo número aleatorio si lo llama en un bucle, una solución ingeniosa es declarar el nuevo objeto Random () fuera del bucle como una variable global.

Tenga en cuenta que debe declarar su instancia de la clase Random fuera de la función GetRandomInt si va a ejecutar esto en un bucle.

"¿Por qué es esto?" usted pregunta.

Bueno, la clase Random en realidad genera números pseudoaleatorios, siendo la "semilla" para el aleatorizador la hora del sistema. Si su ciclo es suficientemente rápido, la hora del reloj del sistema no aparecerá diferente al aleatorizador y cada nueva instancia de la clase Aleatoria comenzaría con la misma semilla y le daría el mismo número pseudoaleatorio.

La fuente está aquí: http://www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/

Ajibola
fuente
Esto es más adecuado como comentario, ya que ni siquiera intenta responder la pregunta real de OP.
b1nary.atr0phy
0

Use un Random estático o los números tienden a repetirse en bucles apretados / rápidos debido a que el reloj del sistema los envía.

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);
    }
    //=-------------------------------------------------------------------
 }

Ver también: https://docs.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.8

David C Fuchs
fuente
-1
Random random = new Random();

double NextDouble(double minimum, double maximum)
{  

    return random.NextDouble()*random.Next(minimum,maximum);

}
Mitesh Savani
fuente