2个双数之间的随机数


156

是否可以生成2个双精度数之间的随机数?

例:

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

然后,我将其称为:

double result = GetRandomNumber(1.23, 5.34);

任何想法将不胜感激。

Answers:


329

是。

Random.NextDouble返回一个介于0和1之间的双精度值。然后将其乘以您需要进入的范围(最大值和最小值之差),然后将其加到基数(最小值)。

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

实际代码应具有随机的静态成员。这将节省创建随机数生成器的成本,并使您可以非常频繁地调用GetRandomNumber。由于我们在每次通话时都会初始化一个新的RNG,因此如果您调用得足够快,以至于两次通话之间的系统时间都不会改变,则RNG将使用完全相同的时间戳进行播种,并生成相同的随机数流。


33
请注意是否在循环中调用GetRandomNumber(),因为它会一遍又一遍地产生相同的值
John Rasch 2009年

13
可能是一个很好的扩展方法,public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5

5
请记住,由于Random.NextDouble永远不会返回1.0,因此此函数也永远不会等于最大数。
马修

6
我建议将新的Random((int)DateTime.Now.Ticks)作为种子放入,即使在快速循环中也应使其“更加随机”。
DanielSkowroński2012年

5
如果最小值为double.MinValue,最大值为double.MaxValue,则此方法不起作用。关于如何处理此用例的任何建议?
Gene S

40

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并重新使用它。


9

注意:如果要在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));
}

这样一来,您可以保证获得不同的double值。


8

最简单的方法将只是生成一个介于0和两个数字之差之间的随机数。然后将两个数字中较小的一个添加到结果中。


3

您可以使用如下代码:

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

2

您可以这样做:

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

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

2

我参加聚会有点晚了,但是我需要实施一个通用的解决方案,结果证明这些解决方案都无法满足我的需求。

公认的解决方案适用于小范围;但是,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.MinValue和之间,这也会很好地生成随机数double.MaxValue。但这引入了另一个“问题”,在本文中很好地介绍了这个问题:如果我们使用如此大的范围,则值似乎太“不自然”。例如,生成10,000个随机双精度double.MaxValue值后,0之间的所有值都在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);
}

一些测试:

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

0..Double.MaxValue

尽管乍看之下线性随机值似乎是错误的,但统计数据表明它们中的任何一个都不比另一个“好”:即使线性策略也具有均匀的分布,并且两种策略的值之间的平均差异几乎相同。

在不同的范围内玩游戏可知,线性策略变得“理智”,范围介于0和ushort.MaxValue“合理”最小值为10.78294704(对于ulong范围,最小值为3.03518E + 15 ; int: 353341)。这些是两种策略以不同的比例显示的相同结果:

0..UInt16.MaxValue


编辑:

最近,我使我的开源,可以随时RandomExtensions.NextDouble通过完整的验证查看该方法。


1

如果值之一为负怎么办?更好的主意是:

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

5
我认为这Math.Abs()是多余的。由于您确保了min >= max,因此max - min无论如何都必须是一个非负数。
Brian Reischl 2013年

1

如果您需要[[ 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)

改用:

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
好点,但是这个命题导致分布偏斜,就像这里的第二个例子stackoverflow.com/a/3365388/3922292
Piotr Falkowski

0

关于在循环中调用它时生成相同的随机数的一个不错的解决方案是,将循环外的新Random()对象声明为全局变量。

注意,如果要在循环中运行此类,则必须在GetRandomInt函数之外声明Random类的实例。

“为什么是这样?” 你问。

好吧,Random类实际上会生成伪随机数,而随机化器的“种子”是系统时间。如果您的循环足够快,则系统时钟时间将不会与随机化器不同,并且Random类的每个新实例都将从相同的种子开始,并为您提供相同的伪随机数。

来源在这里:http : //www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/


这更适合作为注释,因为它甚至没有尝试回答OP的实际问题。
b1nary.atr0phy

0

使用静态随机数,否则由于系统时钟为它们播种,因此数字倾向于在紧密/快速循环中重复。

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 : //docs.microsoft.com/zh-cn/dotnet/api/system.random?view=netframework-4.8


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.