根据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
对象,你的随机数生成器将在其余生命周期中仅返回0。
还需要注意的一点是,连续创建多个Random
对象 - 即使使用时间作为种子 - 可能会导致相同的伪随机数序列。
Random r1 = new Random();
Random r2 = new Random();
Random r3 = new Random();
Random r4 = new Random();
在上面的代码中,很有可能
r1
、
r2
、
r3
和
r4
都会生成相同的序列。
这是怎么可能的呢?因为CPU非常快。1 GHz的CPU可以每秒执行大约10亿条指令(多少不等),也就是每1纳秒执行1条指令,或者说每1百万分之1毫秒执行1条指令。创建一个新的
Random
对象可能需要很多指令,但肯定不会超过100万条。
那么为什么我们需要手动定义种子呢?如果使用时钟的当前毫秒计数是我们“所有人”想要的,并且已经是默认值,为什么还需要手动定义种子呢?
因为它对于保持多个终端同步非常有用。
想象一下一个游戏,重要的现象随机出现,例如天气变化可能完全颠覆游戏。你不希望只有一方受到雾的影响,而其他人仍然从晴朗的天气中获益,对吧?
当然,你可以让服务器或主机生成随机的天气变化并通知玩家;或者你可以在游戏开始前定义一个种子,并使用该种子来确保整个游戏中所有玩家的“随机性”相同。
编程是不是很有趣呢?
rnd
声明为static readonly
并重复使用同一实例。 - Alessandro D'Andria