Nombre aléatoire entre 2 nombres doubles


156

Est-il possible de générer un nombre aléatoire entre 2 doubles?

Exemple:

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

Ensuite, je l'appelle avec ce qui suit:

double result = GetRandomNumber(1.23, 5.34);

Toute réflexion sera apprécié.

Réponses:


329

Oui.

Random.NextDouble renvoie un double entre 0 et 1. Vous multipliez ensuite cela par la plage dans laquelle vous devez entrer (différence entre le maximum et le minimum), puis ajoutez cela à la base (minimum).

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

Le code réel doit avoir random être un membre statique. Cela économisera le coût de création du générateur de nombres aléatoires et vous permettra d'appeler GetRandomNumber très fréquemment. Puisque nous initialisons un nouveau RNG à chaque appel, si vous appelez assez rapidement pour que l'heure système ne change pas entre les appels, le RNG sera amorcé avec exactement le même horodatage et générera le même flux de nombres aléatoires.


33
Faites juste attention si vous appelez GetRandomNumber () dans une boucle car il générera la même valeur encore et encore
John Rasch

13
Pourrait être une bonne méthode d'extension,public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5

5
Gardez à l'esprit que puisque Random.NextDouble ne renvoie jamais 1.0, cette fonction n'égalera jamais non plus le nombre maximum.
Matthieu

6
Je suggère de mettre comme graine un nouveau Random ((int) DateTime.Now.Ticks) cela devrait le rendre "plus aléatoire" même dans les boucles rapides.
Daniel Skowroński

5
Cela ne fonctionne pas si la valeur minimale est double.MinValue et la valeur maximale est double.MaxValue. Des suggestions sur la façon de gérer ce cas d'utilisation?
Gene S

40

Johnny5 a suggéré de créer une méthode d'extension. Voici un exemple de code plus complet montrant comment procéder:

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

Vous pouvez maintenant l'appeler comme s'il s'agissait d'une méthode sur la Randomclasse:

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

Notez que vous ne devez pas créer beaucoup de nouveaux Randomobjets dans une boucle car cela rendra probable que vous obteniez la même valeur plusieurs fois de suite. Si vous avez besoin de beaucoup de nombres aléatoires, créez une instance de Randomet réutilisez-la.


9

Attention: si vous générez l' randomintérieur d'une boucle comme par exemple for(int i = 0; i < 10; i++), ne mettez pas la new Random()déclaration à l'intérieur de la boucle.

Depuis MSDN :

La génération de nombres aléatoires commence à partir d'une valeur de départ. Si la même valeur de départ est utilisée à plusieurs reprises, la même série de nombres est générée. Une façon de produire différentes séquences consiste à rendre la valeur de départ dépendante du temps, produisant ainsi une série différente avec chaque nouvelle instance de Random. Par défaut, le constructeur sans paramètre de la classe Random utilise l'horloge système pour générer sa valeur de départ ...

Donc, sur la base de ce fait, faites quelque chose comme:

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

En procédant de cette façon, vous avez la garantie d'obtenir différentes valeurs doubles.


8

L'approche la plus simple générerait simplement un nombre aléatoire entre 0 et la différence des deux nombres. Ajoutez ensuite le plus petit des deux nombres au résultat.


3

Vous pouvez utiliser un code comme celui-ci:

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

2

Vous pouvez faire ceci:

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

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

2

Je suis un peu en retard à la fête mais j'avais besoin de mettre en place une solution générale et il s'est avéré qu'aucune des solutions ne peut satisfaire mes besoins.

La solution acceptée est bonne pour les petites plages; cependant, maximum - minimumpeut être l'infini pour les grandes plages. Donc une version corrigée peut être cette version:

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

Cela génère des nombres aléatoires bien même entre double.MinValueet double.MaxValue. Mais cela introduit un autre "problème", qui est bien présenté dans cet article : si nous utilisons de si grandes plages, les valeurs peuvent sembler trop "artificielles". Par exemple, après avoir généré 10000 doubles aléatoires entre 0 etdouble.MaxValue toutes les valeurs étaient comprises entre 2,9579E + 304 et 1,7976E + 308.

J'ai donc créé une autre version, qui génère des nombres sur une échelle logarithmique:

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

Quelques tests:

Voici les résultats triés de la génération de 10 000 nombres doubles aléatoires entre 0 et Double.MaxValueavec les deux stratégies. Les résultats sont affichés avec une échelle logarithmique:

0..Double.MaxValue

Bien que les valeurs aléatoires linéaires semblent erronées à première vue, les statistiques montrent qu'aucune d'elles n'est "meilleure" que l'autre: même la stratégie linéaire a une distribution égale et la différence moyenne entre les valeurs est à peu près la même avec les deux stratégies .

Jouer avec différentes plages m'a montré que la stratégie linéaire devient "saine" avec une plage comprise entre 0 et ushort.MaxValueune valeur minimum "raisonnable" de 10,78294704 (pour une ulongplage, la valeur minimum était 3,03518E + 15 int;: 353341). Ce sont les mêmes résultats des deux stratégies affichées avec des échelles différentes:

0..UInt16.MaxValue


Éditer:

Récemment, j'ai rendu mes bibliothèques open source, n'hésitez pas à voir la RandomExtensions.NextDoubleméthode avec la validation complète.


1

Que faire si l'une des valeurs est négative? Ne serait pas une meilleure idée:

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

5
Je pense que Math.Abs()c'est redondant. Puisque vous vous êtes assuré que min >= max, alors max - mindoit être un nombre non négatif de toute façon.
Brian Reischl

1

Si vous avez besoin d'un nombre aléatoire dans la plage [ 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)

Utilisez à la place:

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
Bon point, mais cette proposition entraîne une distribution biaisée comme dans le deuxième exemple ici stackoverflow.com/a/3365388/3922292
Piotr Falkowski

0

À propos de la génération du même nombre aléatoire si vous l'appelez dans une boucle, une solution astucieuse consiste à déclarer le nouvel objet Random () en dehors de la boucle en tant que variable globale.

Notez que vous devez déclarer votre instance de la classe Random en dehors de la fonction GetRandomInt si vous allez l'exécuter dans une boucle.

"Pourquoi est-ce?" tu demandes.

Eh bien, la classe Random génère en fait des nombres pseudo aléatoires, la «graine» du randomiseur étant l'heure système. Si votre boucle est suffisamment rapide, l'heure de l'horloge système n'apparaîtra pas différente de celle du randomiseur et chaque nouvelle instance de la classe Random commencerait avec la même graine et vous donnerait le même nombre pseudo aléatoire.

La source est ici: http://www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/


Ceci est mieux adapté comme commentaire, car il n'essaie même pas de répondre à la question réelle d'OP.
b1nary.atr0phy

0

Utilisez un aléatoire statique ou les nombres ont tendance à se répéter dans des boucles serrées / rapides en raison de l'horloge système les ensemencant.

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

Voir aussi: https://docs.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.8


-1
Random random = new Random();

double NextDouble(double minimum, double maximum)
{  

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

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.