在两个双精度数之间生成随机数

199

能否生成两个 double 之间的随机数?

例如:

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

然后我用以下方式调用它:

double result = GetRandomNumber(1.23, 5.34);

希望得到任何想法。

13个回答

412

是的。

Random.NextDouble返回介于0和1之间的double类型数值。然后,您需要将其乘以所需范围(最大值与最小值之间的差)并加上基数(最小值)。

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

真实的代码应该将随机数生成器设置为静态成员。这将节省创建随机数生成器的开销,并使您能够频繁调用GetRandomNumber。因为我们在每次调用时都要初始化一个新的随机数生成器,如果您快速调用,以至于系统时间在调用之间没有更改,那么随机数生成器将使用完全相同的时间戳进行种子化,并生成相同的随机数流。


46
如果您在循环中调用GetRandomNumber(),请注意它将一遍又一遍地生成相同的值。 - John Rasch
13
这可能是一个不错的扩展方法,public double GetRandomNumber(this Random random, double minimum, double maximum) {...} - Johnny5
6
请记住,由于 Random.NextDouble 永远不会返回1.0,因此该函数也永远不会等于最大数。 - Matthew
6
我建议将一个以当前时间刻度整数值为种子的新Random对象作为随机数生成器的种子,这样即使在快速循环中,也能产生更加随机的结果。 - Daniel Skowroński
6
如果最小值是double.MinValue,最大值是double.MaxValue,那么这个方法将无法工作。有关如何处理此用例的建议是什么? - Gene S
显示剩余10条评论

53

Johnny5建议创建一个扩展方法。以下是更完整的代码示例,展示如何实现:

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

现在你可以像调用Random类的方法一样调用它:
Random random = new Random();
double value = random.NextDouble(1.23, 5.34);

请注意,在循环中不要创建大量的Random对象,因为这会使得你连续多次获得相同的值的可能性增加。如果你需要大量的随机数,则应创建一个Random实例并重复使用它。

12

注意:如果你在循环中生成random(例如for(int i = 0; i < 10; i++)),不要将new Random()声明放在循环内部。

根据MSDN的说明:

随机数生成从种子值开始。 如果重复使用相同的种子,则生成相同的数字序列。 产生不同序列的一种方法是使种子值与时间有关,从而在每个新的Random实例中生成不同的序列。 默认情况下,Random类的无参数构造函数使用系统时钟来生成其种子值...

因此,基于这个事实,应该这样做:

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

采用这种方式,您可以确保获得不同的双精度值。


11

我有点晚了,但我需要实现一个通用的解决方案,结果发现没有一个解决方案能满足我的需求。

接受的解决方案适用于小范围;然而,对于大范围来说,maximum - minimum 可能是无限的。因此,一种更正的版本可以是这个版本:

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

这个方法可以在double.MinValuedouble.MaxValue之间生成随机数。但是,这也带来了另一个“问题”,正如此帖子中所明确的那样:如果使用这么大的范围,生成的值可能会显得过于“不自然”。例如,在生成0到double.MaxValue之间的10,000个随机双精度浮点数后,所有的值都在2.9579E+304和1.7976E+308之间。
因此,我还创建了另一个版本,它以对数比例生成数字:
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);
}

一些测试:

这里是使用两种策略生成 0 到 Double.MaxValue 之间的 10,000 个随机双精度数的排序结果。结果使用对数刻度显示:

0..Double.MaxValue

虽然线性随机值乍一看似乎不正确,但统计数据表明它们中没有一个比另一个“更好”:即使线性策略具有均匀分布,两种策略之间的平均差异也基本相同。

通过尝试不同的范围,我发现线性策略在 0 到 ushort.MaxValue 范围内变得“合理”,最小值为 10.78294704(对于 ulong 范围,最小值为 3.03518E+15;int: 353341)。这些是两种策略的相同结果,但使用不同的刻度显示:

0..UInt16.MaxValue


编辑:

最近我将我的开源了,可以查看完整验证的RandomExtensions.NextDouble 方法。


8

最简单的方法就是生成一个介于两个数字之差之间的随机数,然后将较小的数字加上结果。


3
您可以使用以下代码:

您可以使用以下代码:

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

3
你可以这样做:
public class RandomNumbers : Random
{
    public RandomNumbers(int seed) : base(seed) { }

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

1
如果其中一个值为负数会怎样呢?那么更好的做法是:
double NextDouble(double min, double max)
{
       if (min >= max)
            throw new ArgumentOutOfRangeException();    
       return random.NextDouble() * (Math.Abs(max-min)) + min;
}

6
我认为 Math.Abs() 是多余的。因为你确保了 min >= max,那么 max - min 无论如何都必须是非负数。 - Brian Reischl

1

使用静态的随机数,否则由于系统时钟的种子,数字往往会在紧密/快速循环中重复出现。

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

参见:https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=netframework-4.8

0
Random random = new Random();

double NextDouble(double minimum, double maximum)
{  

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

}

这将不能创建平均分布。结果会严重偏向于接近0的数字。 - Tim Pohlmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接