每次运行应用程序时生成相同数字的随机数生成器

我知道有很多次这个问题已经提出,但这些解决方案都不适用于我。

首先,我在我的方法中做了这个名为RandomNumGenerator(items)

 List randNum = new List(); foreach (var item in items) { randNum.Add(new Random(1000).Next()); } 

这总是给了我相同的数字,然后在看了这个答案之后我做了这个:

 Random rnd = new Random(1000); foreach (var item in items) { randNum.Add(rnd.Next()); } 

这给了我以下数字

 325467165 506683626 1623525913 2344573 1485571032 

现在虽然循环的每次迭代都没问题,但问题是,当我停止并重新运行应用程序时,我得到了我之前重新获得的相同数字。

 325467165 506683626 1623525913 2344573 1485571032 

这是在调试期间的行为,还是每次调用RandomNumGenerator都会出现相同的问题?

您在这里始终使用相同的种子1000为Random实例播种:

 Random rnd = new Random(1000); 

由于当前时间用作种子,因此不会这样做:

 Random rnd = new Random(); 

看看带有int的构造函数 。

向不同的随机对象提供相同的种子值使得每个实例产生相同的随机数序列

根据MSDN 。

 public Random( int Seed ) 

种子

用于计算伪随机数序列的起始值的数字。 如果指定了负数,则使用该数字的绝对值。

大多数初学者涉及RNG(随机数发生器)的错误的原因是缺乏对“种子”是什么以及它起什么作用的理解。


什么 “种子”?

Random类是一个用于生成伪随机数的类 – 或者看似随机的数字。 它们通常是一个数学函数,它使用一个参数 – “种子” – 来生成一系列似乎是随机的数字。

new Random(1000)的情况下,前5个非负随机整数是

325467165
506683626
1623525913
2344573
1485571032

在你的第一个代码中,每次需要一个随机数时,你都会使用相同的种子创建一个新的伪随机数序列,所以很明显你的数组用相同的数字填充: 325467165 ,它恰好是第一个生成的非负整数new Random(1000)

这也解释了为什么每次启动应用程序时,第二个代码总是生成相同的伪随机数序列。

为确保您的应用始终生成不同的伪随机序列,您每次都需要使用不同的种子。 到目前为止,确保这一点的最简单方法是从字面上节省时间

 Random rnd = new Random(DateTime.UtcNow.Millisecond); // Taking the millisecond component, because it changes quickly 

幸运的是,你不必输入这么多,因为Random类的默认构造函数已经做了类似的操作。

 Random rnd = new Random(); // Much simpler, isn't it? 

请记住, Random不是线程安全的; 如果多个线程同时尝试访问同一个Random对象,则RNG将在其生命周期的剩余时间内仅返回0。

另外需要注意的是,一个接一个地创建多个Random对象 – 即使使用时间作为种子 – 也可以导致相同的伪随机数序列。

 Random r1 = new Random(); Random r2 = new Random(); Random r3 = new Random(); Random r4 = new Random(); 

在上面的代码中,机会非常高r1r2r3r4都将生成相同的序列。

怎么可能?
好吧,(联合国)幸运的是,CPU正在快速发展。 1 GHz CPU每秒可执行大约10亿条指令(给予或接受); 这是每1纳秒的1条指令 – 或每百万分之一毫秒的1条指令。
创建一个新的Random对象可能需要很多指令,但绝大多数都不到一百万。


那么为什么我们需要手动定义一个种子,如果使用时钟的当前毫秒数是我们“所有”想要的并且已经是默认值?

因为它对于保持多个终端同步非常有用。

想象一下游戏中随机出现的重要现象,例如可能完全颠覆游戏的天气变化。 你不希望只有一方遭受大雾,而其他方面仍然可以从晴朗的天气中获利,对吗?

当然,您可以让服务器或主机生成随机天气变化并通知玩家; 或者您可以在游戏开始之前定义种子,并使用该种子确保整个游戏中所有玩家的“随机性”相同。

编码不好玩吗?

你需要改变这个:

 Random rnd = new Random(1000); 

 Random rnd = new Random(); 

从Random Constructor docs :

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

关键概念是随机种子 – 随机衍生其他所有东西的初始数据。 如果种子相同,那么“随机”序列将是相同的。

默认情况下,种子设置为零,这显然会导致程序运行中重复序列。

为避免这种情况,您可以像这样构建Random:

 Random rnd = new Random(); 

……在引擎盖下,是:

 Random rnd = new Random(Environment.TickCount); 

这将从OS启动开始以毫秒为单位初始化Random对象。 每次程序启动时都会有所不同,因此每次都会得到不同的随机序列。

Random .Next()方法生成伪随机数。 您应该声明并初始化随机对象,而不是每次创建新对象。 而且不需要使用任何Cryctography .. 🙂

您应该使用类级随机变量。 如果您在方法级别使用新的Random作为本地,则时间相关的种子将重复生成相同的随机数序列。

 class Program { static Random _r = new Random(); static void Main() { // use _r variable to generate random number } }