web-dev-qa-db-fra.com

Nombre aléatoire entre 2 numéros doubles

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 comme suit:

double result = GetRandomNumber(1.23, 5.34);

Toute réflexion sera apprécié.

134
CodeLikeBeaker

Oui.

Random.NextDouble renvoie un double entre 0 et 1. Vous multipliez ensuite cette valeur par la plage dans laquelle vous devez entrer (différence entre le maximum et le minimum), puis vous l'ajoutez à 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 au hasard un membre statique. Cela réduira les coûts de création du générateur de nombres aléatoires et vous permettra d'appeler très fréquemment GetRandomNumber. Étant donné que nous initialisons un nouveau générateur RNG à chaque appel, si vous appelez suffisamment rapidement pour que l'heure du système ne change pas entre les appels, le générateur RNG reçoit le même horodatage exact et génère le même flux de nombres aléatoires.

298
Michael

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 de la classe Random:

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

Notez que vous ne devez pas créer beaucoup de nouveaux objets Random dans une boucle, car il est probable que vous obtiendrez la même valeur plusieurs fois de suite. Si vous avez besoin de beaucoup de nombres aléatoires, créez une instance de Random et réutilisez-la.

38
Mark Byers

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

8
Greg D

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

De MSDN :

La génération de nombres aléatoires commence à partir d'une valeur de départ. Si la même graine 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, basé sur 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 ainsi, vous avez la garantie d'obtenir différentes valeurs doubles.

7
Leniel Maccaferri

Vous pouvez utiliser un code comme ceci:

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

Vous pourriez 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
user490775

Et si une des valeurs est négative? Une meilleure idée ne serait-elle pas:

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

Si vous avez besoin d’un nombre aléatoire compris entre [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;
        }
    }
}
1
alex

Pour générer le 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 souhaitez l'exécuter en boucle.

"Pourquoi est-ce?" Vous demandez.

Eh bien, la classe Random génère en fait des nombres pseudo aléatoires, la "graine" de l’algorithme étant l’heure système. Si votre boucle est suffisamment rapide, l’horloge système n’apparaîtra pas différemment de l’aléaliseur et chaque nouvelle instance de la classe Random démarrera avec le même germe et vous donnera 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/

0
Ajibola

Je suis un peu en retard pour le parti mais je devais mettre en place une solution générale et il s'est avéré qu'aucune des solutions ne pouvait satisfaire mes besoins.

La solution acceptée est bonne pour les petites gammes; toutefois, maximum - minimum peut ê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 bien des nombres aléatoires même entre double.MinValue et double.MaxValue. Mais ceci introduit un autre "problème", qui est bien présenté dans cet article : si nous utilisons de telles étendues, les valeurs pourraient sembler trop "non naturelles". Par exemple, après avoir généré 10 000 doubles aléatoires entre 0 et double.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 doubles nombres aléatoires entre 0 et Double.MaxValue avec 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 fausses au premier abord, les statistiques montrent qu'aucune d'entre 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 sensiblement la même avec les deux stratégies. .

Jouer avec différentes gammes m'a montré que la stratégie linéaire devient "saine" avec une plage entre 0 et ushort.MaxValue avec une valeur minimale "raisonnable" de 10,78294704 (pour ulong, la valeur minimale é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


Modifier:

Récemment, j'ai créé mon bibliothèques open source, n'hésitez pas à consulter le RandomExtensions.NextDouble méthode avec la validation complète.

0
György Kőszeg