Zufallszahl zwischen 2 doppelten Zahlen

156

Ist es möglich, eine Zufallszahl zwischen 2 Doppelwerten zu generieren?

Beispiel:

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

Dann nenne ich es mit folgendem:

double result = GetRandomNumber(1.23, 5.34);

Alle Gedanken wäre dankbar.

CodeLikeBeaker
quelle

Antworten:

329

Ja.

Random.NextDouble gibt ein Double zwischen 0 und 1 zurück. Dann multiplizieren Sie das mit dem Bereich, in den Sie gehen müssen (Differenz zwischen Maximum und Minimum) und addieren dieses dann zur Basis (Minimum).

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

Realer Code sollte zufällig ein statisches Mitglied sein. Dies spart die Kosten für die Erstellung des Zufallszahlengenerators und ermöglicht es Ihnen, GetRandomNumber sehr häufig aufzurufen. Da wir bei jedem Anruf ein neues RNG initialisieren, wird das RNG mit genau demselben Zeitstempel gesetzt und generiert denselben Zufallszahlenstrom, wenn Sie schnell genug anrufen, damit sich die Systemzeit zwischen den Anrufen nicht ändert.

Michael
quelle
33
Achten Sie darauf, wenn Sie GetRandomNumber () in einer Schleife aufrufen, da dies immer wieder denselben Wert generiert
John Rasch
13
Könnte eine schöne Verlängerungsmethode sein,public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5
5
Beachten Sie, dass Random.NextDouble niemals 1.0 zurückgibt und diese Funktion auch niemals der maximalen Anzahl entspricht.
Matthew
6
Ich schlage vor, als Startwert neue Random ((int) DateTime.Now.Ticks) zu setzen, dies sollte es auch in schnellen Schleifen "zufälliger" machen.
Daniel Skowroński
5
Dies funktioniert nicht, wenn der minimale Wert double.MinValue und der maximale Wert double.MaxValue ist. Irgendwelche Vorschläge, wie man mit diesem Anwendungsfall umgeht?
Gene S
40

Johnny5 schlug vor, eine Erweiterungsmethode zu erstellen. Hier ist ein vollständigeres Codebeispiel, das zeigt, wie Sie dies tun können:

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

Jetzt können Sie es so aufrufen, als wäre es eine Methode für die RandomKlasse:

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

Beachten Sie, dass Sie nicht viele neue RandomObjekte in einer Schleife erstellen sollten, da dies wahrscheinlich dazu führt, dass Sie mehrmals hintereinander denselben Wert erhalten. Wenn Sie viele Zufallszahlen benötigen, erstellen Sie eine Instanz von Randomund verwenden Sie sie erneut.

Mark Byers
quelle
9

Achtung: Wenn Sie das randomInnere einer Schleife generieren , wie zum Beispiel for(int i = 0; i < 10; i++), setzen Sie die new Random()Deklaration nicht in die Schleife.

Von MSDN :

Die Zufallszahlengenerierung beginnt mit einem Startwert. Wenn derselbe Startwert wiederholt verwendet wird, wird dieselbe Zahlenreihe generiert. Eine Möglichkeit, unterschiedliche Sequenzen zu erzeugen, besteht darin, den Startwert zeitabhängig zu machen, wodurch mit jeder neuen Instanz von Random eine andere Reihe erzeugt wird. Standardmäßig verwendet der parameterlose Konstruktor der Random-Klasse die Systemuhr, um den Startwert zu generieren ...

Gehen Sie also aufgrund dieser Tatsache wie folgt vor:

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

Auf diese Weise haben Sie die Garantie, dass Sie unterschiedliche Doppelwerte erhalten.

Leniel Maccaferri
quelle
8

Der einfachste Ansatz würde einfach eine Zufallszahl zwischen 0 und der Differenz der beiden Zahlen erzeugen. Fügen Sie dann die kleinere der beiden Zahlen zum Ergebnis hinzu.

Greg D.
quelle
3

Sie könnten folgenden Code verwenden:

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

Sie könnten dies tun:

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

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

Ich bin etwas spät dran, aber ich musste eine allgemeine Lösung implementieren, und es stellte sich heraus, dass keine der Lösungen meine Anforderungen erfüllen kann.

Die akzeptierte Lösung ist gut für kleine Bereiche; jedoch maximum - minimumkann es sich unendlich für große Bereiche. Eine korrigierte Version kann also diese Version sein:

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

Dies erzeugt auch zwischen double.MinValueund schöne Zufallszahlen double.MaxValue. Dies führt jedoch zu einem weiteren "Problem", das in diesem Beitrag gut dargestellt wird : Wenn wir so große Bereiche verwenden, erscheinen die Werte möglicherweise zu "unnatürlich". Zum Beispiel double.MaxValuelagen nach der Erzeugung von 10.000 zufälligen Doppelwerten zwischen 0 und allen Werten zwischen 2,9579E + 304 und 1,7976E + 308.

Also habe ich auch eine andere Version erstellt, die Zahlen auf einer logarithmischen Skala generiert:

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

Einige Tests:

Hier sind die sortierten Ergebnisse der Erzeugung von 10.000 zufälligen Doppelzahlen zwischen 0 und Double.MaxValuemit beiden Strategien. Die Ergebnisse werden mit logarithmischer Skala angezeigt:

0..Double.MaxValue

Obwohl die linearen Zufallswerte auf den ersten Blick falsch zu sein scheinen, zeigen die Statistiken, dass keiner von ihnen "besser" ist als der andere: Selbst die lineare Strategie hat eine gleichmäßige Verteilung und die durchschnittliche Differenz zwischen den Werten ist bei beiden Strategien ziemlich gleich .

Das Spielen mit verschiedenen Bereichen hat mir gezeigt, dass die lineare Strategie mit einem Bereich zwischen 0 und ushort.MaxValueeinem "angemessenen" Mindestwert von 10,78294704 "vernünftig" wird (für den ulongBereich betrug der Mindestwert 3,03518E + 15 ; int: 353341). Dies sind die gleichen Ergebnisse beider Strategien, die mit unterschiedlichen Maßstäben angezeigt werden:

0..UInt16.MaxValue


Bearbeiten:

Kürzlich habe ich meine Bibliotheken zu Open Source gemacht. Sie können sich die RandomExtensions.NextDoubleMethode mit der vollständigen Validierung ansehen .

György Kőszeg
quelle
1

Was ist, wenn einer der Werte negativ ist? Wäre eine bessere Idee nicht:

double NextDouble(double min, double max)
{
       if (min >= max)
            throw new ArgumentOutOfRangeException();    
       return random.NextDouble() * (Math.Abs(max-min)) + min;
}
Chris Susie
quelle
5
Ich denke das Math.Abs()ist überflüssig. Da Sie das sichergestellt haben min >= max, max - minmuss es dann sowieso eine nicht negative Zahl sein.
Brian Reischl
1

Wenn Sie eine Zufallszahl im Bereich [ 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)

Verwenden Sie stattdessen:

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
quelle
2
Guter Punkt, aber dieser Vorschlag führt zu einer verzerrten Verteilung wie im zweiten Beispiel hier stackoverflow.com/a/3365388/3922292
Piotr Falkowski
0

Wenn Sie dieselbe Zufallszahl in einer Schleife aufrufen möchten, können Sie das neue Random () -Objekt außerhalb der Schleife als globale Variable deklarieren.

Beachten Sie, dass Sie Ihre Instanz der Random-Klasse außerhalb der GetRandomInt-Funktion deklarieren müssen, wenn Sie dies in einer Schleife ausführen möchten.

"Warum ist das?" du fragst.

Nun, die Random-Klasse generiert tatsächlich Pseudozufallszahlen, wobei der „Startwert“ für den Randomizer die Systemzeit ist. Wenn Ihre Schleife ausreichend schnell ist, unterscheidet sich die Systemuhrzeit nicht vom Randomizer, und jede neue Instanz der Random-Klasse beginnt mit demselben Startwert und gibt Ihnen dieselbe Pseudozufallszahl.

Quelle ist hier: http://www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/

Ajibola
quelle
Dies ist besser als Kommentar geeignet, da nicht einmal versucht wird, die eigentliche Frage von OP zu beantworten.
b1nary.atr0phy
0

Verwenden Sie einen statischen Zufall, oder die Zahlen wiederholen sich in engen / schnellen Schleifen, da die Systemuhr sie setzt.

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

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

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

double NextDouble(double minimum, double maximum)
{  

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

}
Mitesh Savani
quelle