【问题标题】:How do I make my C# random number generator change with each time the constructor is called?每次调用构造函数时,如何使我的 C# 随机数生成器发生变化?
【发布时间】:2012-01-20 22:46:01
【问题描述】:

我有两个班级,一个教师班级和一个学生班级。在我的程序中,我创建了一个由 10 个教师对象组成的数组,每个教师对象中有一个由 10 个学生对象组成的数组。每个 Student 对象也有一个整数数组作为成员变量,当每个 Student 被实例化时,它自己的整数数组会填充来自随机数生成器的数字。我的程序是这样的:

  • 创建了一个大小为 10 的教师类型数组
  • 然后用 10 个实际教师对象填充数组
  • 每个教师对象都包含一个大小为 10 的学生对象数组作为成员变量
  • 每个 Teacher 对象中的 Student 数组都填充了实际的 Student 对象
  • 每个学生对象都有一个整数数组,在学生对象构造函数中填充随机数。

这是我遇到的问题:似乎每次为每个教师创建 10 个学生对象时,即使我调用 .Next() 函数,学生对象构造函数中的随机数生成器也不会重置或更改直到为下一个教师对象创建下一组 10 个学生对象。我想要的是 10 个教师对象,每个对象都有自己的学生对象,这些对象有自己的整数数组,其中填充了随机生成的数字。

任何帮助将不胜感激!我在最后一个问题中遇到了构造函数的此类问题,这是一个问题是否是静态的,我不确定这次是不是这样。如果我对任何事情不够清楚,请向我提问!

更新** 所以在查看了 MSDN 之后,我在他们的示例代码“Thread.Sleep(2000)”中找到了并将其插入到我的学生构造函数中,只是为了看看它做了什么。尽管我的程序现在运行速度慢了很多,但似乎已经解决了这个问题,是否有一个最小睡眠值来等待 Random.Next() 使用时钟中的新种子,即使我确实解决了问题,是否有更好的方法来做到这一点?我的随机数生成器已经是 Student 的静态成员变量了。

【问题讨论】:

  • 请分享您使用的代码Random
  • Random 对象在哪里?声明和使用它的代码是什么样的?

标签: c# random static constructor


【解决方案1】:

如果您在短时间内使用默认构造函数 Random() 初始化多个 Random 实例,那么您可能会冒着它们最终获得相同种子值(取决于时间)的风险,因此,每次生成相同的随机序列。

您可以通过初始化单个静态Random 实例并在所有Student 实例之间共享它来解决此问题。只要您不是多线程,这是绝对安全的。

public class Student
{
    private static readonly Random random = new Random();        
}

来自 Random() 构造函数的 MSDN 页面:

默认种子值来自系统时钟并且具有有限的分辨率。因此,通过调用默认构造函数连续创建的不同Random 对象将具有相同的默认种子值,因此将产生相同的随机数集。这个问题可以通过使用单个Random 对象来生成所有随机数来避免。您也可以通过修改系统时钟返回的种子值,然后将这个新的种子值显式提供给Random(Int32) 构造函数来解决此问题。有关详细信息,请参阅 Random(Int32) 构造函数。

编辑:尽管您声明您的随机数生成器已经是一个静态成员,但如果您在每次 Student 初始化期间重复(冗余)实例化它,这仍然是不够的。以下代码仍然不正确

public class Student
{
    private static Random random;

    private int[] numbers;

    public Student()
    {
        random = new Random();
        numbers = new int[10];
        for (int i = 0; i < 10; ++i)
            numbers[i] = random.Next();
    }
}

您需要将其替换为仅实例化一次 Random 的版本,如下所示:

public class Student
{
    private static readonly Random random = new Random();

    private int[] numbers;

    public Student()
    {
        numbers = new int[10];
        for (int i = 0; i < 10; ++i)
            numbers[i] = random.Next();
    }
}

【讨论】:

  • 我的学生类已经有一个静态随机对象。我的问题会是进程运行速度远远快于计算机时钟吗?我将如何解决这个问题?感谢您的帮助
  • 不,Next 方法不依赖于时间信息。会不会是您在反复实例化 Random 对象,尽管它是静态的?你能粘贴你有new Random()的代码部分吗?
【解决方案2】:

Random 的一个常见缺陷是每次需要随机值时都创建一个新实例。

for (int i = 0; i < 1000; i++) {
   var random = new Random();
   list.Add(random.Next());
}

这不会按预期工作,因为Random 使用实际时间来创建第一个随机值。但是,时钟的分辨率有限。可能是几个后续迭代返回相同的时间,因此返回相同的(伪)随机值!

解决方案是只创建一次random

var random = new Random();
for (int i = 0; i < 1000; i++) {
   list.Add(random.Next());
}

您也可以将random 声明为静态类成员

public static readonly Random RandomGenerator = new Random();

【讨论】:

  • 或者传入一个对现有随机生成器public Student(Random randomGenerator)的引用
  • 如果 Microsoft 在 Random 类中提供静态属性,这将是一个很好且简单的解决方案。 int x = Random.Default.Next();
  • @OlivierJacot-Descombes:您可以通过封装自己完成。
  • @Boo:这就是我在答案的最后一行所做的。我的意思是,为什么每个程序员都必须再次解决它?如果解决方案在 BCL 中会很方便。
  • @OlivierJacot-Descombes 它不存在,因为从多个线程调用 Next() 是不安全的。
【解决方案3】:

随机数生成器使用系统时间作为种子。我想知道您是否可能声明一个新的生成器并在系统有时间进入下一个增量之前调用它。处理器周期实际上比系统时钟快得多。可以发个代码示例吗?

我建议将静态生成器声明为类属性或其他可访问的位置,将其实例化为 final。这将保证你不会用另一个覆盖生成器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-12
    • 2017-09-11
    • 2020-03-05
    • 2015-08-16
    • 2011-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多