Số ngẫu nhiên giữa 2 số kép


156

Có thể tạo ra một số ngẫu nhiên giữa 2 nhân đôi?

Thí dụ:

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

Sau đó, tôi gọi nó với như sau:

double result = GetRandomNumber(1.23, 5.34);

Bất kỳ suy nghĩ sẽ được đánh giá cao.

Câu trả lời:


329

Đúng.

Random.NextDouble trả về gấp đôi từ 0 đến 1. Sau đó, bạn nhân số đó với phạm vi bạn cần đi vào (chênh lệch giữa tối đa và tối thiểu) và sau đó thêm nó vào cơ sở (tối thiểu).

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

Mã thực nên có ngẫu nhiên là một thành viên tĩnh. Điều này sẽ tiết kiệm chi phí tạo trình tạo số ngẫu nhiên và sẽ cho phép bạn gọi GetRandomNumber rất thường xuyên. Vì chúng tôi đang khởi tạo RNG mới với mỗi cuộc gọi, nếu bạn gọi đủ nhanh để thời gian hệ thống không thay đổi giữa các cuộc gọi, RNG sẽ được tạo hạt giống với cùng dấu thời gian chính xác và tạo ra cùng một dòng số ngẫu nhiên.


33
Chỉ cần xem nếu bạn gọi GetRandomNumber () trong một vòng lặp vì nó sẽ tạo ra cùng một giá trị lặp đi lặp lại
John Rasch

13
Có thể là một phương pháp kéo dài tốt đẹp,public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5

5
Hãy nhớ rằng vì Random.NextDouble không bao giờ trả về 1.0, nên hàm này cũng sẽ không bao giờ bằng số tối đa.
Matthew

6
Tôi đề nghị đặt làm hạt giống ngẫu nhiên mới ((int) DateTime.Now.Ticks) điều này sẽ làm cho nó "ngẫu nhiên hơn" ngay cả trong các vòng lặp nhanh.
Daniel Skowroński

5
Điều này không hoạt động nếu giá trị tối thiểu là double.MinValue và giá trị tối đa là double.MaxValue. Bất kỳ đề xuất về cách xử lý trường hợp sử dụng này?
Gene S

40

Johnny5 đề nghị tạo ra một phương pháp mở rộng. Đây là một ví dụ mã đầy đủ hơn cho thấy cách bạn có thể làm điều này:

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

Bây giờ bạn có thể gọi nó như thể nó là một phương thức trên Randomlớp:

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

Lưu ý rằng bạn không nên tạo nhiều Randomđối tượng mới trong một vòng lặp bởi vì điều này sẽ khiến bạn có thể nhận được cùng một giá trị nhiều lần liên tiếp. Nếu bạn cần nhiều số ngẫu nhiên thì hãy tạo một thể hiện Randomvà sử dụng lại nó.


9

Xem ra: nếu bạn đang tạo randomvòng lặp bên trong như ví dụ for(int i = 0; i < 10; i++), đừng đặt new Random()khai báo bên trong vòng lặp.

Từ MSDN :

Việc tạo số ngẫu nhiên bắt đầu từ một giá trị hạt giống. Nếu cùng một hạt giống được sử dụng lặp đi lặp lại, cùng một chuỗi số được tạo ra. Một cách để tạo ra các chuỗi khác nhau là làm cho giá trị hạt giống phụ thuộc vào thời gian, từ đó tạo ra một chuỗi khác nhau với mỗi trường hợp ngẫu nhiên mới. Theo mặc định, hàm tạo không tham số của lớp Ngẫu nhiên sử dụng đồng hồ hệ thống để tạo giá trị hạt giống của nó ...

Vì vậy, dựa trên thực tế này, làm một cái gì đó như:

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

Làm theo cách này bạn có đảm bảo bạn sẽ nhận được các giá trị kép khác nhau.


8

Cách tiếp cận đơn giản nhất chỉ đơn giản là tạo ra một số ngẫu nhiên giữa 0 và sự khác biệt của hai số. Sau đó thêm số nhỏ hơn của hai số vào kết quả.


3

Bạn có thể sử dụng mã như thế này:

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

2

Bạn có thể làm điều này:

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

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

2

Tôi đến bữa tiệc hơi muộn nhưng tôi cần thực hiện một giải pháp chung và hóa ra không có giải pháp nào có thể thỏa mãn nhu cầu của tôi.

Các giải pháp được chấp nhận là tốt cho phạm vi nhỏ; tuy nhiên, maximum - minimumcó thể là vô hạn cho phạm vi lớn. Vì vậy, một phiên bản sửa chữa có thể là phiên bản này:

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

Điều này tạo ra số ngẫu nhiên độc đáo ngay cả giữa double.MinValuedouble.MaxValue. Nhưng điều này giới thiệu một "vấn đề" khác, được trình bày độc đáo trong bài viết này : nếu chúng ta sử dụng phạm vi lớn như vậy, các giá trị có vẻ quá "không tự nhiên". Ví dụ: sau khi tạo 10.000 nhân đôi ngẫu nhiên giữa 0 và double.MaxValuetất cả các giá trị nằm trong khoảng từ 2.9579E + 304 đến 1.7976E + 308.

Vì vậy, tôi cũng tạo ra một phiên bản khác, tạo ra các số theo thang logarit:

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

Một số xét nghiệm:

Dưới đây là kết quả được sắp xếp của việc tạo 10.000 số kép ngẫu nhiên trong khoảng từ 0 đến Double.MaxValuevới cả hai chiến lược. Các kết quả được hiển thị với việc sử dụng thang đo logarit:

0..Double.MaxValue

Mặc dù các giá trị ngẫu nhiên tuyến tính dường như sai ngay từ cái nhìn đầu tiên, các thống kê cho thấy rằng không có giá trị nào trong số chúng "tốt hơn" so với cái khác: ngay cả chiến lược tuyến tính có phân phối đồng đều và sự khác biệt trung bình giữa các giá trị khá giống nhau với cả hai chiến lược .

Chơi với các phạm vi khác nhau cho tôi thấy rằng chiến lược tuyến tính trở nên "lành mạnh" với phạm vi từ 0 đến ushort.MaxValuevới giá trị tối thiểu "hợp lý" là 10,78294704 (đối với ulongphạm vi giá trị tối thiểu là 3.03518E + 15 ; int: 353341). Đây là cùng một kết quả của cả hai chiến lược được hiển thị với các tỷ lệ khác nhau:

0..UInt16.MaxValue


Biên tập:

Gần đây tôi đã làm cho các thư viện của mình là nguồn mở, hãy xem RandomExtensions.NextDoublephương thức với xác nhận hoàn chỉnh.


1

Nếu một trong các giá trị là âm thì sao? Sẽ không phải là một ý tưởng tốt hơn là:

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

5
Tôi nghĩ Math.Abs()là dư thừa. Vì bạn đảm bảo rằng min >= max, max - mindù sao cũng phải là một số không âm.
Brian Reischl

1

Nếu bạn cần một số ngẫu nhiên trong phạm vi [ 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)

Sử dụng thay thế:

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
Điểm tốt, nhưng đề xuất này dẫn đến phân phối sai lệch như trong ví dụ thứ hai ở đây stackoverflow.com/a/3365388/3922292
Piotr Falkowski

0

Về việc tạo cùng một số ngẫu nhiên nếu bạn gọi nó trong một vòng lặp, một giải pháp tiện lợi là khai báo đối tượng Random () mới bên ngoài vòng lặp là một biến toàn cục.

Lưu ý rằng bạn phải khai báo thể hiện của lớp Random trong hàm GetRandomInt nếu bạn sẽ chạy nó trong một vòng lặp.

Tại sao lại thế này? bạn hỏi.

Chà, lớp Ngẫu nhiên thực sự tạo ra các số ngẫu nhiên giả, với hạt giống trực tiếp cho bộ ngẫu nhiên là thời gian hệ thống. Nếu vòng lặp của bạn đủ nhanh, thời gian đồng hồ hệ thống sẽ không xuất hiện khác với bộ ngẫu nhiên và mỗi phiên bản mới của lớp Ngẫu nhiên sẽ bắt đầu với cùng một hạt giống và cung cấp cho bạn số ngẫu nhiên giả giống nhau.

Nguồn ở đây: http://www.whypad.com/posts/csharp-get-a-random-number-b between-x-and-y / 412 /


Điều này phù hợp hơn như là một nhận xét, vì nó thậm chí không cố gắng trả lời câu hỏi thực tế của OP.
b1nary.atr0phy

0

Sử dụng Ngẫu nhiên tĩnh hoặc các số có xu hướng lặp lại trong các vòng lặp chặt / nhanh do đồng hồ hệ thống gieo chúng.

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

Xem thêm: 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);

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.