.net 如何在C#中生成0到1之间的随机数?

1szpjjfi  于 12个月前  发布在  .NET
关注(0)|答案(6)|浏览(229)

我想得到1和0之间的随机数。但我每次都得0分。有人能解释一下为什么我总是得0分吗?这是我试过的代码。

Random random = new Random();
int test = random.Next(0, 1);
Console.WriteLine(test);
Console.ReadKey();
2exbekwf

2exbekwf1#

根据documentationNext返回一个介于(含)最小值和(不含)最大值之间的整数随机数:

返回值

大于或等于minValue且小于maxValue的32位有符号整数;也就是说,返回值的范围包括minValue但不包括maxValue。如果minValue等于maxValue,则返回minValue。
唯一能满足的整数

0 <= x < 1

0,因此你总是得到值0。换而言之,0是在半闭区间[0, 1)内的唯一整数。
所以,如果你真的对整数值01感兴趣,那么使用2作为上限:

var n = random.Next(0, 2);

如果你想得到一个0到1之间的小数,试试:

var n = random.NextDouble();
oxf4rvwz

oxf4rvwz2#

好吧,我怀疑你想生成一个随机的单位间隔。这在数学、统计学、逻辑学和物理学中随处可见,所以这看起来是一个非常合乎逻辑的请求。
如果这就是你想要的,那么这一页上关于双打的每一个答案都是错误的。这有点搞笑,因为每个人都在引用文档。如果你使用NextDouble()生成一个double,你不会得到一个0到1之间的数字,你会得到一个从0到1之间的数字。如果你想要一个单位间隔,那就不好了。(也许你只是想抛硬币,你的问题有点模糊……)

很久以后的增编

我开始了一个关于 * 为什么 * 在{0,1}的范围内获得一个随机包含的浮点数真的很难做到。跳到底部的一个聪明的黑客,将做的伎俩,如果你只是想解决这个问题,并继续生活。

回到牧场

为了得到一个双倍,你必须做一些像这样的技巧:

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

然后调用

NextRandomRange(0,1 + Double.Epsilon);

看起来这很管用不是吗1 + Double。当使用double时,Eppery应该是1之后的第二大数字,对吗?这就是你如何解决整数问题。
好啊好啊...
我怀疑这不会正确工作,因为底层代码将生成几个字节的随机性,然后做一些数学技巧来使其符合预期的范围。简短的回答是,应用于int的逻辑在处理浮点数时并不完全相同。
我们去看看,好吗?(https://referenceslib.microsoft.com/#mscorlib/system/random.cs,e137873446fcef75)

/*=====================================Next=====================================
  **Returns: A double [0..1)
  **Arguments: None
  **Exceptions: None
  ==============================================================================*/
  public virtual double NextDouble() {
    return Sample();
  }

什么是Sample()?

/*====================================Sample====================================
  **Action: Return a new random number [0..1) and reSeed the Seed array.
  **Returns: A double [0..1)
  **Arguments: None
  **Exceptions: None
  ==============================================================================*/
  protected virtual double Sample() {
      //Including this division at the end gives us significantly improved
      //random number distribution.
      return (InternalSample()*(1.0/MBIG));
  }

好吧,开始有进展了。MBIG是Int32.MaxValue(2147483647或2^31-1),使得除法运算为:

InternalSample()*0.0000000004656612873077392578125;

什么是InternalSample()?

private int InternalSample() {
      int retVal;
      int locINext = inext;
      int locINextp = inextp;

      if (++locINext >=56) locINext=1;
      if (++locINextp>= 56) locINextp = 1;
      
      retVal = SeedArray[locINext]-SeedArray[locINextp];

      if (retVal == MBIG) retVal--;          
      if (retVal<0) retVal+=MBIG;
      
      SeedArray[locINext]=retVal;

      inext = locINext;
      inextp = locINextp;
                
      return retVal;
  }

这还真有点意思但是这个种子阵列和inext垃圾是什么意思?

private int inext;
  private int inextp;
  private int[] SeedArray = new int[56];

所以事情就开始走到一起了。种子数组是一个int数组,用于从中生成值。如果你看一下init函数def,你会发现有很多位加法和技巧被用来随机化一个有55个值的数组,初始值是准随机的。

public Random(int Seed) {
    int ii;
    int mj, mk;

    //Initialize our Seed array.
    //This algorithm comes from Numerical Recipes in C (2nd Ed.)
    int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
    mj = MSEED - subtraction;
    SeedArray[55]=mj;
    mk=1;
    for (int i=1; i<55; i++) {  //Apparently the range [1..55] is special (All hail Knuth!) and so we're skipping over the 0th position.
      ii = (21*i)%55;
      SeedArray[ii]=mk;
      mk = mj - mk;
      if (mk<0) mk+=MBIG;
      mj=SeedArray[ii];
    }
    for (int k=1; k<5; k++) {
      for (int i=1; i<56; i++) {
    SeedArray[i] -= SeedArray[1+(i+30)%55];
    if (SeedArray[i]<0) SeedArray[i]+=MBIG;
      }
    }
    inext=0;
    inextp = 21;
    Seed = 1;
  }

好了,回到InternalSample(),我们现在可以看到,随机双精度数是通过取两个加扰的32位整数之差,将结果固定在0到2147483647 - 1的范围内,然后将结果乘以1/2147483647来生成的。当它使用值时,会使用更多的技巧来打乱种子值列表,但本质上就是这样。
(It有趣的是,得到这个范围内任何数字的概率大约是1/r,除了2^31-2,它是2 *(1/r)!因此,如果你认为一些愚蠢的编码器正在使用RandNext()在视频扑克机上生成数字,你应该始终下注2^32-2!这就是为什么我们不把随机用于任何重要的事情的原因之一。
因此,如果InternalSample()的输出为0,我们将其乘以0.000000004656612873077392578125,得到0,即范围的底端。如果我们得到2147483646,我们最终得到0.9999999995343387126922607421875,所以NextDouble产生结果[0,1)的说法是......对吗?更准确地说,它是[0,0.999999995343387126922607421875]的整数范围。
我上面建议的解决方案会落在它的脸上,因为double. Epperin = 4.94065645841247E-324,这比0.0000000004656612873077392578125小得多(你会把我们上面的结果加到1上)。
具有讽刺意味的是,如果不是在InternalSample()方法中减去1:

if (retVal == MBIG) retVal--;

我们可以在返回值中得到1。因此,要么复制Random类中的所有代码并省略retVal--行,要么将NextDouble()输出乘以类似1.000000004656612875245796924106的值,以稍微拉伸输出,使其在范围内包含1。实际上,测试这个值让我们非常接近,但我不知道我运行的数亿次测试是否没有产生2147483646(很有可能),或者有一个浮点错误进入了等式。我怀疑是前者。数百万次的测试不太可能产生20亿分之一的结果。

// Just try to explain where you got this number during the code review...
NextRandomRange(0,1.0000000004656612875245796924106);

TLDR?

包含随机双精度值的包含范围比较棘手。
但如果你还在听我说,或者你只是因为读了上面的注解而跳到这里,这里有一个非常简单的解决方案,它不需要你有离散浮点数学的博士学位。

黑客

如果在{0,1}之间生成一个浮点数非常困难,让我们生成一个int并转换它。

float fauxFloat = random.Next(0,11)/10;

哦,鬼鬼祟祟的。现在,这不会给你给予太多的精确度,所以我们可以通过简单地添加更多内容来改进它:

public double RandomUnitInterval()
{
    var_Random = new Random();
    int precision = 1000000000;
    return (random.Next(0, precision + 1)) / ((double)precision);
}

如果你将精度设置为10,并循环它,你会看到我们偶尔会同时到达0和1。使命完成。这个示例方法可以让您获得超过小数点后9位的精度,因此对于大多数应用程序来说,它应该非常可靠。

这就是你如何得到一个{0,1} (含) 之间的随机数!

3hvapo4f

3hvapo4f3#

你可以这样做,但你应该这样做:

double test = random.NextDouble();

如果你想得到随机整数(0或1),你应该设置上限为2,因为它是排他性的

int test = random.Next(0, 2);
k10s72fa

k10s72fa4#

你得到零是因为Random.Next(a,b)返回范围[a,b)中的数字,它大于或等于a,小于b。
如果要获取{0,1}之一,应使用用途:

var random = new Random();
var test = random.Next(0, 2);
nmpmafwu

nmpmafwu5#

因为你要的是一个小于1的数。
documentation说道:

返回值

大于或等于minValue且小于maxValue的32位有符号整数;也就是说,返回值的范围包括minValue但不包括maxValue。如果minValue等于maxValue,则返回minValue。

vaqhlq81

vaqhlq816#

如果您的目标是0.0到1.0,请像这样重写代码

Random random = new Random();

double test = random.NextDouble();

Console.WriteLine(test);

Console.ReadKey();

相关问题