【问题标题】:Why does it appear that my random number generator isn't random in C#?为什么我的随机数生成器在 C# 中看起来不是随机的?
【发布时间】:2010-10-30 06:31:00
【问题描述】:

我正在使用 Microsoft Visual C# 2008 Express。

我找到了这个sn-p的代码:

    public static int RandomNumber(int min, int max)
    {
        Random random = new Random();

        return random.Next(min, max);
    }

问题是我已经运行了超过 100 次,当我的 min = 0 和 max = 1 时,它总是给我相同的答案。我每次都得到 0。 (我创建了一个测试函数来运行它——真的——我每次都得到 0)。我很难相信这是一个巧合......我还能做些什么来检查或测试这个吗? (我确实用 min = 0 和 max = 10 重新运行了测试,前 50 次,结果总是“5”,第二次 50 次,结果总是“9”。

??我需要一些更一致的随机...

-阿迪娜

【问题讨论】:

  • 出于某种原因,我认为“始终如一地随机”真的很有趣。
  • 我想补充一点,根据您的应用程序的目的,使用伪随机将始终(至少在理论上)使您的应用程序可破解。

标签: c# random


【解决方案1】:

您的范围不正确。 minValue 包含在范围内,而 maxValue 在范围内不包含(意味着它不会包含在范围内)。所以这就是它只返回 0 的原因。

另一个有用的注意事项:在方法中创建一个 Random 实例并不理想,因为它可能会在调用时获得相同的种子。所以我会说使用:

static Random gen = new Random();

public static int RandomNumber(int minValue, int maxValue){
    return gen.Next(minValue, maxValue);
}

【讨论】:

    【解决方案2】:

    您误解了“random.Next(min, max)”这一行。 “min”代替允许随机生成的最小数字。虽然“max”是在不允许生成的最小数字的位置,但它不是在允许绘制的最大数字的位置。所以当这条线是 random.Next(0, 1) 你基本上只允许绘制 0。

    【讨论】:

      【解决方案3】:

      我找到了一种非常简单但有效的方法来生成随机数,只需获取当前日期时间毫秒的最后两位数:

         int seed = Convert.ToInt32(DateTime.Now.Millisecond.ToString().Substring(1, 2));
         int cnr = new Random(seed).Next(100);
      

      它很粗糙,但它有效! :-)

      当然,它会在统计上每一百次生成相同的数字。或者,您可以采用所有三位数字或与其他日期时间值(例如秒左右)连接。

      【讨论】:

        【解决方案4】:

        你总是得到 0,因为 Random.Next 返回整数。您需要调用Random.NextDouble,它将返回一个介于 0 和 1 之间的数字。此外,您应该重用 Random 实例,如下所示:

        [ThreadStatic]
        static Random random;
        public static Random Random { 
            get {
                if (random == null) random = new Random();
                return random;
            }
        }
        public static int RandomInteger(int min, int max)
        {
            return Random.Next(min, max);
        }
        public static double RandomDouble() //Between 0 and 1
        { 
            return Random.NextDouble();
        } 
        

        如果您想要加密安全的随机数,请使用 RNGCryptoServiceProvider 类;见this article

        编辑:线程安全

        【讨论】:

        • -1 因为这段代码是危险的错误。 Random 不是线程安全的,您正在访问共享实例而不提供任何锁定或每个线程实例。
        【解决方案5】:

        几位张贴者声明 Random() 使用基于系统时钟上当前秒的种子,并且在同一秒内创建的任何其他 Random 实例将具有相同的种子。这是不正确的。 Random 的无参数构造函数的种子基于滴答计数或自启动时间以来的毫秒数。此值在大多数系统上大约每 15 毫秒更新一次,但可能会因硬件和系统设置而异。

        【讨论】:

          【解决方案6】:

          这是任何答案的附录,因为这个特定问题的答案是边界应该是 (0, 2) 而不是 (0, 1)。

          但是,如果您想使用静态包装方法,那么您必须记住Random 不是线程安全的,因此您需要提供自己的同步机制或提供每个线程的实例。这是一个很大程度上非阻塞的实现,它使用一个生成器来为每个线程生成器播种:

          public static class ThreadSafeRandom
          {
              private static readonly Random seed = new Random();
          
              [ThreadStatic]
              private static Random random;
          
              public static int Next(int min, int max)
              {
                  if (random == null)
                  {
                      lock (seed)
                      {
                          random = new Random(seed.Next());
                      }
                  }
          
                  return random.Next(min, max);
              }
          
              // etc. for other members
          }
          

          【讨论】:

          • Environment.TickCount (默认种子)对于种子来说已经足够了;制作单独的种子实例没有意义。
          • @SLaks - 如果你依赖 Environment.TickCount 那么你很可能会为每个线程实例获得相同的种子,这将导致它们产生相同的伪随机值序列。我非常怀疑这是你想要的。通过给他们一个明确不同的种子,你可以防止这种行为模式的发生。
          【解决方案7】:
          random = new Random();
          

          这会以当前时间(以秒为单位)启动随机数生成器。当您在系统时钟更改之前多次调用您的函数时,随机数生成器会使用相同的值启动,因此它会返回相同的值序列。

          【讨论】:

          【解决方案8】:

          正如其他人所提到的,每秒构建多次的 Random 使用与种子相同的秒数,所以我将 Random 构造函数放在循环之外,并将其作为参数传递,如下所示:

          public static int RandomNumber(Random random, int min, int max)
          {
              return random.Next(min, max);
          }
          

          正如其他人所提到的,最大值是独占的,所以如果你想要一个 0 或 1,你应该使用 [0,2] 作为你的 [min,max],或者更大的最大值,然后使用二进制与1.

          public static int RandomOneOrZero(Random random)
          {
              return random.Next(0, int.MaxValue) & 1;
          }
          

          【讨论】:

          • Random对象也可以声明为静态变量,不需要每次都传递给方法。
          • 我喜欢 RandomOneOrZero 方法。您使用它是因为您知道它比使用 [0,2] 更随机还是出于其他原因?
          • @Matt:无论何时,随机性都是一样的。我只是把它从我的头顶上掀了起来。 @Whatsit:好点。我不确定她的用例是什么,所以我只是猜测了一下。静态或实例变量可能更合适。
          【解决方案9】:

          除了其他答案中已经提到的 0-1 问题之外,当您正在寻找 0-10 范围并连续 50 次获得相同结果时,您的问题是真正的问题。

          new Random() 应该返回一个随机数,种子从计时器初始化(当前秒),但显然你每秒调用此代码 50 次。 MSDN 建议:“为了提高性能,创建一个 Random 以随着时间的推移生成许多随机数,而不是重复创建一个新的 Random 以生成一个随机数。”。如果您在方法之外创建了随机生成器,那应该可以解决您的“非随机性”问题并提高性能。

          如果您需要“更高质量”的伪随机数,还可以考虑使用this post 以获得比系统提供的更好的伪随机数生成器。

          【讨论】:

            【解决方案10】:

            不要为 Next 创建包装方法。它浪费了创建 Random 类的新实例的周期。就用同一个吧!

            Random myRand = new Random();
            
            for(int i = 0; i < 10; i++)
            {
                Console.WriteLine(myRand.Next(0, 10).ToString());
            }
            

            这应该给你十个随机值。

            如前所述——Random 是伪随机的(就像所有实现一样),如果您使用相同的种子创建 100 个实例,您将获得 100 个具有相同结果的实例。确保您正在重用该类。

            另外,正如人们所说,要注意 MinValue 是包容性的,而 MaxValue 是排斥性的。对于你想要的,执行 myRand.Next(0, 2)。

            【讨论】:

            • 做 10 次或更多次只是测试......我没有意识到“max”是排他性的。
            • 有时你会想要一个 Random 的包装器方法(或者更可能是一个包装器类),以便你可以模拟它。
            • @adeena, BlackWasp:如果您出于任何原因需要重载/包装 Next,那就去做吧。但是,如果您只想获得另一个随机值,请使用 Next 并使用相同的 Random 类。这样一来,您就不会冒太多风险获得一堆具有相同种子的 Random。
            【解决方案11】:

            Next() 的重载返回:

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

            0 是它返回的唯一可能值。也许你想要 random.NextDouble(),它会返回一个介于 0 和 1 之间的双精度数。

            【讨论】:

              【解决方案12】:

              min = 0 和 max = 1 的问题是 min 是包含的,而 max 是排除的。因此,该组合的唯一可能值是 0。

              【讨论】:

              • 为了让事情更加混乱,文档指出 maxValue 必须大于或等于 minValue。因此,您可以传入 random.Next(0,0),它的功能与 random.Next(0,1) 完全相同,即使没有与指定输入参数匹配的数字。
              【解决方案13】:

              最小值包含在内,但最大值不包含在内。 Check out the API

              【讨论】:

                【解决方案14】:

                在 VB 中,我总是从 Randomize() 函数开始。只需调用 Randomize() 然后运行您的随机函数。我还执行以下操作:

                Function RandomInt(ByVal lower As Integer, ByVal upper As Integer) As Integer
                    Return CInt(Int((upper - lower + 1) * Rnd() + lower))
                End Function
                

                希望这会有所帮助! :)

                【讨论】:

                • 你是真的吗?你知道我有多少次得到 VB 问题的 C# 答案并且能够弄清楚我做错了什么吗?这是概念,伙计,不是语法...天哪...
                • 这些函数中的每一个仅在 VB 中可用(除非您引用 Microsoft.VisualBasic.dll)
                • System.Random 类完全不同。
                猜你喜欢
                • 1970-01-01
                • 2011-05-21
                • 1970-01-01
                • 2023-03-19
                • 1970-01-01
                • 2021-02-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多