Nomor Acak Antara 2 Angka Ganda

156

Apakah mungkin untuk menghasilkan angka acak antara 2 ganda?

Contoh:

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

Lalu saya menyebutnya dengan yang berikut:

double result = GetRandomNumber(1.23, 5.34);

Pikiran apa pun akan dihargai.

CodeLikeBeaker
sumber

Jawaban:

329

Iya.

Random.NextDouble mengembalikan dua kali lipat antara 0 dan 1. Anda kemudian kalikan dengan rentang yang Anda butuhkan (perbedaan antara maksimum dan minimum) dan kemudian tambahkan ke basis (minimum).

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

Kode nyata seharusnya acak menjadi anggota statis. Ini akan menghemat biaya pembuatan generator angka acak, dan akan memungkinkan Anda untuk memanggil GetRandomNumber sangat sering. Karena kami menginisialisasi RNG baru dengan setiap panggilan, jika Anda menelepon cukup cepat sehingga waktu sistem tidak berubah di antara panggilan, RNG akan diunggulkan dengan stempel waktu yang sama persis, dan menghasilkan aliran angka acak yang sama.

Michael
sumber
33
Hanya hati-hati jika Anda memanggil GetRandomNumber () dalam satu lingkaran karena akan menghasilkan nilai yang sama berulang kali
John Rasch
13
Bisa menjadi metode ekstensi yang bagus,public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5
5
Ingatlah bahwa sejak Random.NextDouble tidak pernah mengembalikan 1.0, fungsi ini juga tidak akan pernah sama dengan angka maksimum.
Matius
6
Saya menyarankan untuk menempatkan sebagai DateTime.Now.Ticks acak baru (int) Random baru ini akan membuatnya "lebih acak" bahkan dalam loop cepat.
Daniel Skowroński
5
Ini tidak berfungsi jika nilai min adalah nilai double.MinValue dan nilai maks adalah double.MaxValue. Ada saran tentang cara menangani use case ini?
Gene S
40

Johnny5 menyarankan untuk membuat metode ekstensi. Berikut contoh kode yang lebih lengkap yang menunjukkan bagaimana Anda bisa melakukan ini:

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

Sekarang Anda dapat menyebutnya seolah-olah itu adalah metode di Randomkelas:

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

Perhatikan bahwa Anda tidak boleh membuat banyak Randomobjek baru dalam satu lingkaran karena ini akan memungkinkan Anda mendapatkan nilai yang sama beberapa kali berturut-turut. Jika Anda membutuhkan banyak angka acak, buat satu contoh Randomdan gunakan kembali.

Mark Byers
sumber
9

Hati-hati: jika Anda membuat randomloop dalam seperti misalnya for(int i = 0; i < 10; i++), jangan meletakkan new Random()deklarasi di dalam loop.

Dari MSDN :

Pembuatan angka acak dimulai dari nilai seed. Jika seed yang sama digunakan berulang kali, serangkaian angka yang sama dihasilkan. Salah satu cara untuk menghasilkan urutan yang berbeda adalah dengan membuat nilai benih bergantung pada waktu, sehingga menghasilkan seri yang berbeda dengan setiap instance acak baru. Secara default, konstruktor tanpa parameter dari kelas Acak menggunakan jam sistem untuk menghasilkan nilai seed-nya ...

Jadi berdasarkan fakta ini, lakukan sesuatu seperti:

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

Dengan melakukan ini, Anda memiliki jaminan Anda akan mendapatkan nilai ganda yang berbeda.

Leniel Maccaferri
sumber
8

Pendekatan paling sederhana hanya akan menghasilkan angka acak antara 0 dan perbedaan dari dua angka. Kemudian tambahkan yang lebih kecil dari dua angka ke hasilnya.

Greg D
sumber
3

Anda dapat menggunakan kode seperti ini:

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

Anda bisa melakukan ini:

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

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

Saya agak terlambat ke pesta tetapi saya perlu menerapkan solusi umum dan ternyata tidak ada solusi yang dapat memenuhi kebutuhan saya.

Solusi yang diterima baik untuk rentang kecil; Namun, maximum - minimumbisa menjadi tak terbatas untuk rentang besar. Jadi versi yang diperbaiki dapat berupa versi ini:

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

Ini menghasilkan angka acak dengan baik bahkan antara double.MinValuedandouble.MaxValue . Tapi ini memperkenalkan "masalah" lain, yang disajikan dengan baik dalam posting ini : jika kita menggunakan rentang besar seperti itu nilainya mungkin terlihat terlalu "tidak wajar". Misalnya, setelah menghasilkan 10.000 double.MaxValuerangkap acak antara 0 dan semua nilai berada di antara 2.9579E + 304 dan 1.7976E + 308.

Jadi saya juga membuat versi lain, yang menghasilkan angka pada skala logaritmik:

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

Beberapa tes:

Berikut adalah hasil yang diurutkan menghasilkan 10.000 angka ganda acak antara 0 dan Double.MaxValuedengan kedua strategi. Hasilnya ditampilkan dengan menggunakan skala logaritmik:

0..Double.MaxValue

Meskipun nilai acak linier tampaknya salah pada pandangan pertama, statistik menunjukkan bahwa tidak ada yang lebih baik daripada yang lain: bahkan strategi linier memiliki distribusi yang merata dan perbedaan rata-rata antara nilai hampir sama dengan kedua strategi .

Bermain dengan rentang yang berbeda menunjukkan kepada saya bahwa strategi linier menjadi "waras" dengan rentang antara 0 dan ushort.MaxValuedengan nilai minimum "masuk akal" dari 10.78294704 (untuk ulongrentang nilai minimum adalah 3.03518E + 15 int;: 353341). Ini adalah hasil yang sama dari kedua strategi yang ditampilkan dengan skala yang berbeda:

0..UInt16.MaxValue


Edit:

Baru-baru ini saya membuat perpustakaan saya open source, merasa bebas untuk melihat RandomExtensions.NextDoublemetode dengan validasi lengkap.

György Kőszeg
sumber
1

Bagaimana jika salah satu nilai negatif? Bukankah ide yang lebih baik:

double NextDouble(double min, double max)
{
       if (min >= max)
            throw new ArgumentOutOfRangeException();    
       return random.NextDouble() * (Math.Abs(max-min)) + min;
}
Chris Susie
sumber
5
Saya pikir Math.Abs()itu berlebihan. Karena Anda memastikan itu min >= max, maka max - minharus menjadi nomor yang tidak negatif.
Brian Reischl
1

Jika Anda memerlukan nomor acak dalam kisaran [ 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)

Gunakan sebaliknya:

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
sumber
2
Poin bagus, tetapi proposisi ini menghasilkan distribusi miring seperti pada contoh kedua di sini stackoverflow.com/a/3365388/3922292
Piotr Falkowski
0

Tentang membuat nomor acak yang sama jika Anda memanggilnya dalam sebuah loop, solusi yang bagus adalah dengan mendeklarasikan objek Random () baru di luar loop sebagai variabel global.

Perhatikan bahwa Anda harus mendeklarasikan instance Anda dari kelas acak di luar fungsi GetRandomInt jika Anda akan menjalankan ini dalam satu lingkaran.

"Kenapa ini?" Anda bertanya.

Nah, kelas Random sebenarnya menghasilkan angka acak semu, dengan "seed" untuk randomizer menjadi waktu sistem. Jika loop Anda cukup cepat, waktu jam sistem tidak akan tampak berbeda dengan randomizer dan setiap instance baru dari kelas Random akan memulai dengan seed yang sama dan memberi Anda nomor acak semu yang sama.

Sumber ada di sini: http://www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/

Ajibola
sumber
Ini lebih cocok sebagai komentar, karena ia bahkan tidak berusaha menjawab pertanyaan OP yang sebenarnya.
b1nary.atr0phy
0

Gunakan Acak statis atau angka-angka cenderung berulang dalam loop ketat / cepat karena jam sistem seeding mereka.

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

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

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

double NextDouble(double minimum, double maximum)
{  

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

}
Mitesh Savani
sumber