【问题标题】:Correct method of a "static" Random.Next in C#?C# 中“静态”Random.Next 的正确方法?
【发布时间】:2011-02-08 06:17:35
【问题描述】:

如果我想创建一个介于 1 和 100 之间的随机数 ....like

,为什么我需要创建一个 Random 类的实例
Random rand = new Random();
rand.Next(1,100);

有没有 Random 类的静态函数可以做同样的事情?喜欢...

Random.Next(1,100);

我不想不必要地创建实例

【问题讨论】:

标签: c# random


【解决方案1】:

在 C# 中创建一个短暂的实例几乎是免费的。不要浪费你的时间担心这个。您可能有更好的地方来寻找性能或内存增益。

【讨论】:

    【解决方案2】:

    最佳做法是创建Random 的单个实例并在整个程序中使用它 - 否则结果可能不会那么随机。不创建静态函数会鼓励这种行为。

    您不必担心“不必要地创建实例”,其影响最多可以忽略不计 - 这是框架的工作方式。

    【讨论】:

    • 如果 Random 是静态的,那会不会实现?生成器将在构建时播种,您可以一直使用它吗?
    • @Asher - 无论如何都需要一个内部实例,并且需要状态。我不认为它属于框架的一部分,但它可以在您的静态类上轻松实现。
    • @Kobi 也许我应该就此发表我自己的问题:)。抛开内存使用和性能不谈,我确实觉得实例化这个类很烦人。如果您只是要始终使用一个,为什么不将其设为静态。这是关于我没有得到的静态类的东西吗?反正我跑题了
    • 想想可重用性 - 认为你只调用了一次,但有一天可能有人会调用你的函数 1000 次,你就会失去随机性。如果你觉得实例化类很烦人,也许 C# 不太适合你 :)
    • @Asher :但是 Random 不是线程安全的,所以如果你想从多个线程调用 Random 是行不通的......此外,如果你想为你的 Random 类提供多个种子怎么办?例如,我想使用多个线程,在每次执行时产生相同的结果,要求我使用具有相同种子的多个实例,使用静态它将是一场比赛。
    【解决方案3】:

    这不是“不必要的”,因为 Random 类在内部存储了一些状态。这样做是为了确保如果您非常快速地多次调用.Next()(在同一毫秒或滴答声或其他任何时间),您仍然不会得到相同的号码。

    当然,如果在你的情况下这不是问题,你总是可以将这两行代码合二为一:

    new Random().Next(1, 100);
    

    【讨论】:

    • 像这样使用 Random 很可能是个问题......如果不是,它可能很快就会成为一个问题 - 如果有人实现这样的随机数生成器,因为它每 50 毫秒被调用一次,并且它可以毫无问题地工作,一旦硬件变得更快,它就会很快损坏,并且有人会花很多时间调试这个:)
    • 没错,所以我永远不会在循环中使用这种代码 - 但如果你知道你的程序只需要生成一个随机数,那么它就足够安全了。
    • 是的,是的,是的,使用它来获取单个项目。从来没有。很想投反对票。
    【解决方案4】:

    为什么不呢?

    您需要创建一个实例,因为生成随机数的方式是先前的答案会影响后续的答案。默认情况下,new Random() 构造函数使用当前系统时间来“播种”序列,但它不是必须的:如果你愿意,你可以传入你自己的数字。特别是:

    var rand = new Random(1234);
    Console.WriteLine(rand.Next(0, 100));
    Console.WriteLine(rand.Next(0, 100));
    Console.WriteLine(rand.Next(0, 100));
    Console.WriteLine(rand.Next(0, 100));
    

    每次都会产生相同的“随机”数字序列。

    这意味着Random 类需要保留实例数据(先前的答案或“种子”)以供后续调用。

    【讨论】:

      【解决方案5】:

      来自MSDN: Random Class (System)

      "随机数的生成是从一个种子值开始的。如果重复使用同一个种子,就会生成相同的序列。产生不同序列的一种方法是使种子值与时间相关,从而产生不同的序列。 Random 的每个新实例的序列。默认情况下,Random 类的无参数构造函数使用系统时钟生成其种子值,而其参数化构造函数可以根据刻度数取一个 Int32 值但是,由于时钟的分辨率是有限的,因此使用无参数构造函数来连续创建不同的 Random 对象会创建生成相同随机数序列的随机数生成器。以下示例说明了连续实例化的两个 Random 对象会生成一系列相同的随机数..."

      Wikipedia explains PRNGs

      【讨论】:

        【解决方案6】:
        //Function to get random number
        private static readonly Random random = new Random();
        private static readonly object syncLock = new object();
        public static int RandomNumber(int min, int max)
        {
            lock(syncLock) { // synchronize
                return random.Next(min, max);
            }
        }
        

        Copied directly from

        【讨论】:

        • 这非常适合避免误用 Evgeny 的回答中的类。 +1 也引用了代码,归功于它的到期:)
        【解决方案7】:

        创建一个新的 Random 实例,然后立即多次调用它,例如:

        for (int i = 0; i < 1000; i++)
        {
             Random rand = new Random();
             Console.WriteLine(rand.Next(1,100));
        }    
        

        将为您提供一个向范围低端加权的分布。

        这样做:

        Random rand = new Random();
        for (int i = 0; i < 1000; i++)
        {
             Console.WriteLine(rand.Next(1,100));
        }    
        

        会给你一个更好的分布。

        【讨论】:

        • 谢谢。不知道。
        【解决方案8】:

        如果你想要你提到的语法,你需要类似的东西。

        namespace MyRandom
        {
            public class Random
            {
                private static m_rand = new Random();
                public static Next(int min, int max)
                {
                    return m_rand.Next(min, max);
                }
            }
        }
        

        这应该可以让你做Random.Next(1,100); 而不必担心播种。

        【讨论】:

        • 您需要 private static System.Random m_rand = new System.Random() 来获得预期的课程。
        【解决方案9】:

        随机数生成器必须保持状态才能“随机”。随机数生成器创建基于随机种子生成的序列。问题是计算机中没有任何东西实际上是随机的。计算机手头最接近的东西是系统时钟。那是该过程发生的有效时间。所以默认使用系统时钟的当前滴答计数。如果您的应用程序足够快,那么许多随机数计算可能会在同一个系统滴答下发生。如果随机数生成器根本不保持状态,它将多次提供相同的随机数(相同的输入给出相同的输出)。这通常不是您想要的。

        我知道它已经回答了,但我只想说在这种情况下我更喜欢使用单例模式。

        【讨论】:

          【解决方案10】:

          您已经在这里得到了答案。只是重申正确的解决方案

          namespace mySpace
          {
              public static class Util
              {
                  private static Random rnd = new Random();
                  public static int GetRandom()
                  {
                      return rnd.Next();
                  }
              }
          }
          

          所以你可以调用:

          var i = Util.GetRandom();
          

          自始至终。 如果你严格需要一个真正的无状态静态方法来生成随机数,你可以依赖Guid

          public static class Util
          {
              public static int GetRandom()
              {
                  return Guid.NewGuid().GetHashCode();
              }
          }
          

          它会慢一点,但可能比Random.Next 随机得多,至少根据我的经验。

          不是

          new Random(Guid.NewGuid().GetHashCode()).Next();
          

          不必要的对象创建会使其变慢,尤其是在循环下。

          而且从不

          new Random().Next();
          

          不仅速度较慢(在循环内),而且它的随机性......在我看来并不是很好......

          【讨论】:

          • Guid.GetHashCode 不保证任何类型的随机性。使用它是一场赌博。我想用new Random之类的美白函数包裹起来就可以了...
          • @usr 我知道 Guid.NewGuid 是为了唯一性而不是随机性,但它的哈希(Guid.GetHashCode)是非常随机的。这是因为 Guid.GetHashCode 基于 Guid 的字符串表示。如果您说字符串的哈希码实现没有记录,因此无法保证行为,我同意,但实际上它是非常随机的,因为它缺乏可预测的模式。
          • 您的第一个示例不是线程安全的。假设你有多个线程调用静态方法,你就会遇到问题。 .Next()is not thread-safe and could feasably throw an IndexOutOfRange exception under concurrent use.
          【解决方案11】:

          最好的方法是拥有一个ThreadStatic Random 实例:

          [ThreadStatic] static Random random;
          
          Random Get() {
           if (random == null) random = new Random(Guid.NewGuid().GetHashCode());
           return random;
          }
          

          这会处理一切

          1. 线程安全
          2. 性能
          3. 无需播种

          我不明白为什么 .NET Framework(以及地球上的任何其他框架)不使用这种精神。

          【讨论】:

          • ThreadStatic(或 ThreadLocal)本身会产生开销,因此它不会处理“所有事情”。
          • 这是真的......但它比每次创建一个新的 Random 实例便宜得多。这与传递随机参考之间的差异非常小。 CoreClr 最近已更改为执行此答案所说的操作,以使随机种子可用于执行 new Random()。 @user2864740
          • (我正在使用这种方法,效果很好且易于推理;只是将我的迂腐本性暴露给普遍性。)
          • 我不同意这种行为 :)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-06
          • 2011-01-17
          • 1970-01-01
          • 2011-05-30
          • 1970-01-01
          相关资源
          最近更新 更多