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

use*_*358 9 .net c# random

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

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

List<int> randNum = new List<int>();
foreach (var item in items)
{
    randNum.Add(new Random(1000).Next());
}
Run Code Online (Sandbox Code Playgroud)

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

Random rnd = new Random(1000);
foreach (var item in items)
{
    randNum.Add(rnd.Next());
}
Run Code Online (Sandbox Code Playgroud)

这给了我以下数字

325467165 
506683626   
1623525913  
2344573     
1485571032
Run Code Online (Sandbox Code Playgroud)

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

325467165 
506683626   
1623525913  
2344573     
1485571032
Run Code Online (Sandbox Code Playgroud)

这种行为只是在调试期间,还是每次调用时都会出现同样的问题RandomNumGenerator

Tim*_*ter 155

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

Random rnd = new Random(1000);
Run Code Online (Sandbox Code Playgroud)

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

Random rnd = new Random();
Run Code Online (Sandbox Code Playgroud)

看一看在构造它接受一个int.

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


Nol*_*nar 41

根据MSDN.

public Random(
    int Seed
)
Run Code Online (Sandbox Code Playgroud)

种子

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

大多数初学者涉及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
Run Code Online (Sandbox Code Playgroud)

幸运的是,你不必键入这么多,因为默认的构造函数Random已经这样做类似的东西.

Random rnd = new Random(); // Much simpler, isn't it?
Run Code Online (Sandbox Code Playgroud)

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

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

Random r1 = new Random();
Random r2 = new Random();
Random r3 = new Random();
Random r4 = new Random();
Run Code Online (Sandbox Code Playgroud)

在上面的代码,机会是非常高的,即r1,r2,r3r4将所有产生的相同序列.

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


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

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

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

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

编码不好玩吗?


Rah*_*thi 19

你需要改变这个:

Random rnd = new Random(1000);
Run Code Online (Sandbox Code Playgroud)

Random rnd = new Random();
Run Code Online (Sandbox Code Playgroud)

随机构造函数文档:

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


Kir*_*kov 5

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

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

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

Random rnd = new Random();
Run Code Online (Sandbox Code Playgroud)

......在引擎盖下,是:

Random rnd = new Random(Environment.TickCount);
Run Code Online (Sandbox Code Playgroud)

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