Numero casuale tra 2 numeri doppi


156

È possibile generare un numero casuale tra 2 doppie?

Esempio:

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

Quindi lo chiamo con il seguente:

double result = GetRandomNumber(1.23, 5.34);

Ogni pensiero sarebbe apprezzato.

Risposte:


329

Sì.

Random.NextDouble restituisce un doppio tra 0 e 1. Quindi moltiplicalo per l'intervallo in cui devi andare (differenza tra massimo e minimo) e poi aggiungi quello alla base (minimo).

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

Il codice reale dovrebbe avere casualmente un membro statico. Ciò consentirà di risparmiare i costi di creazione del generatore di numeri casuali e ti consentirà di chiamare GetRandomNumber molto frequentemente. Poiché stiamo inizializzando un nuovo RNG ad ogni chiamata, se si chiama abbastanza rapidamente da non cambiare l'ora del sistema tra le chiamate, l'RNG verrà seminato con lo stesso identico timestamp e genererà lo stesso flusso di numeri casuali.


33
Fai attenzione se chiami GetRandomNumber () in un ciclo in quanto genererà lo stesso valore più e più volte
John Rasch,

13
Potrebbe essere un buon metodo di estensione,public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5,

5
Tieni presente che poiché Random.NextDouble non restituisce mai 1.0, anche questa funzione non equivale mai al numero massimo.
Matteo,

6
Suggerisco di mettere come seme un nuovo Random ((int) DateTime.Now.Ticks) questo dovrebbe renderlo "più casuale" anche in cicli rapidi.
Daniel Skowroński,

5
Questo non funziona se il valore minimo è double.MinValue e il valore massimo è double.MaxValue. Qualche suggerimento su come gestire questo caso d'uso?
Gene S

40

Johnny5 ha suggerito di creare un metodo di estensione. Ecco un esempio di codice più completo che mostra come è possibile farlo:

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

Ora puoi chiamarlo come se fosse un metodo sulla Randomclasse:

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

Nota che non dovresti creare molti nuovi Randomoggetti in un ciclo perché questo renderà probabile che tu ottenga lo stesso valore più volte di seguito. Se hai bisogno di molti numeri casuali, crea un'istanza Randome riutilizzala.


9

Attenzione: se stai generando l' randominterno di un ciclo come ad esempio for(int i = 0; i < 10; i++), non inserire la new Random()dichiarazione all'interno del ciclo.

Da MSDN :

La generazione di numeri casuali inizia da un valore seed. Se lo stesso seme viene utilizzato ripetutamente, viene generata la stessa serie di numeri. Un modo per produrre sequenze diverse è rendere il valore del seme dipendente dal tempo, producendo così una serie diversa con ogni nuova istanza di Random. Per impostazione predefinita, il costruttore senza parametri della classe Random utilizza l'orologio di sistema per generare il suo valore seed ...

Quindi, in base a questo fatto, fai qualcosa come:

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

In questo modo hai la garanzia di ottenere valori doppi diversi.


8

L'approccio più semplice genererebbe semplicemente un numero casuale compreso tra 0 e la differenza dei due numeri. Quindi aggiungi il più piccolo dei due numeri al risultato.


3

È possibile utilizzare il codice in questo modo:

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

2

Potresti farlo:

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

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

2

Sono un po 'in ritardo alla festa, ma avevo bisogno di implementare una soluzione generale e si è scoperto che nessuna delle soluzioni è in grado di soddisfare le mie esigenze.

La soluzione accettata è buona per piccole gamme; tuttavia, maximum - minimumpuò essere infinito per grandi gamme. Quindi una versione corretta può essere questa versione:

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

Questo genera numeri casuali piacevolmente anche tra double.MinValuee double.MaxValue. Ma questo introduce un altro "problema", che è ben presentato in questo post : se utilizziamo intervalli così grandi i valori potrebbero sembrare troppo "innaturali". Ad esempio, dopo aver generato 10.000 doppi casuali tra 0 e double.MaxValuetutti i valori erano compresi tra 2.9579E + 304 e 1.7976E + 308.

Quindi ho creato anche un'altra versione, che genera numeri su scala logaritmica:

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

Alcuni test:

Ecco i risultati ordinati per la generazione di 10.000 numeri doppi casuali tra 0 e Double.MaxValuecon entrambe le strategie. I risultati vengono visualizzati utilizzando la scala logaritmica:

0..Double.MaxValue

Sebbene a prima vista i valori casuali lineari sembrino sbagliati, le statistiche mostrano che nessuno di essi è "migliore" dell'altro: anche la strategia lineare ha una distribuzione uniforme e la differenza media tra i valori è praticamente la stessa con entrambe le strategie .

Giocare con range diversi mi ha mostrato che la strategia lineare diventa "sana" con range compreso tra 0 e ushort.MaxValuecon un valore minimo "ragionevole" di 10.78294704 (per ulongrange il valore minimo era 3.03518E + 15 int;: 353341). Questi sono gli stessi risultati di entrambe le strategie visualizzate con scale diverse:

0..UInt16.MaxValue


Modificare:

Recentemente ho reso le mie librerie open source, sentiti libero di vedere il RandomExtensions.NextDoublemetodo con la validazione completa.


1

Cosa succede se uno dei valori è negativo? Un'idea migliore sarebbe:

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

5
Penso che Math.Abs()sia ridondante. Dato che lo hai assicurato min >= max, allora max - mindeve essere comunque un numero non negativo.
Brian Reischl,

1

Se hai bisogno di un numero casuale nell'intervallo [ 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)

Usa invece:

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
Buon punto, ma questa proposta comporta una distribuzione distorta come nel secondo esempio qui stackoverflow.com/a/3365388/3922292
Piotr Falkowski

0

Informazioni sulla generazione dello stesso numero casuale se lo si chiama in un ciclo una soluzione ingegnosa è dichiarare il nuovo oggetto Random () al di fuori del ciclo come variabile globale.

Nota che devi dichiarare la tua istanza della classe Random al di fuori della funzione GetRandomInt se la eseguirai in un ciclo.

"Perchè è questo?" tu chiedi.

Bene, la classe Random in realtà genera numeri pseudo casuali, con il "seed" per il randomizer che è il tempo di sistema. Se il tuo loop è sufficientemente veloce, il tempo di clock del sistema non apparirà diverso dal randomizzatore e ogni nuova istanza della classe Random inizierà con lo stesso seed e ti darà lo stesso numero pseudo random.

La fonte è qui: http://www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/


Questo è più adatto come commento, in quanto non tenta nemmeno di rispondere alla domanda effettiva di OP.
b1nary.atr0phy

0

Utilizzare un Casuale statico oppure i numeri tendono a ripetersi in loop stretti / veloci a causa del clock di sistema che li semina.

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

Vedi anche: 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);

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.