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

本文关键字:数字 运行 应用程序 随机数生成器 | 更新日期: 2023-09-27 17:54:41

我知道这个问题已经提过很多次了,但是这些解决方案都不适合我。

首先我在我的方法RandomNumGenerator(items)

中做了这个
List<int> randNum = new List<int>();
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时都会遇到相同的问题?

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

您正在播种Random实例总是在这里使用相同的种子1000:

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 百万分之一毫秒一个指令。
创建一个新的Random对象可能需要相当多的指令,但绝对比少。


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

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

想象一个游戏,重要的现象随机出现,比如天气的变化可能完全颠覆游戏。你不会希望只有一侧受到雾霾的影响,而其余部分仍然受益于晴朗的天气,对吧?

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

编程不是很有趣吗?

你需要改变这个:

Random rnd = new Random(1000);

Random rnd = new Random();

来自随机构造函数文档:

默认种子值来自系统时钟,并且具有有限的决议。中创建的不同的Random对象对默认构造函数调用的紧密连续将具有相同的默认种子值,因此将产生相同的一组随机数。这个问题可以通过使用单个随机对象生成所有随机数。你也可以变通它通过修改种子值返回系统时钟,然后显式地将这个新的种子值提供给Random(Int32)构造函数。有关更多信息,请参阅Random(Int32)构造函数。

关键概念是随机种子-随机派生其他所有内容的初始数据块。如果种子相同,则"随机"序列将相同。

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

为了避免这种情况,你可以这样构造你的Random:

Random rnd = new Random();

…这是,在引擎盖下,是:

Random rnd = new Random(Environment.TickCount);

这将初始化随机对象从操作系统启动的毫秒数。每次程序启动时,这将是不同的,所以每次您将得到不同的随机序列。

Random . next()方法生成伪随机数。你应该声明并初始化一个随机对象,而不是每次都创建一个新对象。而且不需要使用任何密码术…:)

您应该使用类级别随机变量。如果您在方法级别使用new Random作为局部,则依赖于时间的种子将重复自身,生成相同的随机数序列。

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