How do I seed a random class to avoid getting duplicate random values
我在静态类的静态方法中有以下代码:
1 2
| Random r = new Random ();
int randomNumber = r .Next(1, 100); |
我把这个放在一个循环里,我一直得到相同的randomNumber!
有什么建议吗?
- 为什么?可以将种子传递给构造函数。您真的需要重新设置它吗?为什么你不能用新的种子创造一个新的种子呢?
- 我不是在抱怨方法/构造函数设计,而是人们如何理解它。他们只看到"new random()"和"r.next",并认为它会为他们选择不同的种子,但事实并非如此。
- 施纳德:人们没有问题,因为他们不能重订一(0)个班级。他们真正的问题是误用了它。
- /同意我的观点。添加一个seed方法,并在本例中重新进行种子植入不能解决问题。种子是基于时间戳的,并且考虑到这个代码在一个紧凑的for循环中运行,以及现代计算机的速度,它将在"同时"重新种子。播种必须进行一次。
对我来说,一个好的种子世代是:
1
| Random rand = new Random (Guid .NewGuid().GetHashCode()); |
这是非常随机的。种子总是不同的,因为种子也是随机生成的。
- +1个优雅的随机发生器!谢谢,这是我谷歌搜索的目的。
- 解决了我的问题,两个应用程序同时启动,获得了完全相同的种子值。
- 从技术上讲,基于guid的种子不是随机的,而是具有一定的唯一性。此外,使用gethashcode()还可以减少唯一性,因为一个guid可以接受比int更多的值。但是,对于许多情况(如果不是大多数情况),这就足够好了。
- @安德烈&233;克里斯托夫安德森,你怎么知道?dilbert.com/dyn/str_strip/000000000/00000000/0000000/0000000/000000/‌&8203;…
- Guid.NewGuid()实际上首先必须使用随机生成器创建guid(以及其他数据,如时间和位置)。而且,它比使用没有参数的new Random()慢,后者从系统时间设置种子,而且随机性也不差。
- 弗雷德漫步到现场,看到一个点坐在笛卡尔飞机的某处。他不知道它是怎么到那里的。就是这样。他重新审视了统治这个特定领域的上帝。"好吧,那是相当随机的!"上下文上帝眨眨眼说,"我没看到那个……"突然,点移动了。弗雷德吓了一跳,注意到了。很快,鲍勃出现了,问道:"嘿,那是怎么到那儿的?今天真奇怪。随机的事情总是发生在我身上。"——如果在从一个环境中观察之前它是进一步随机的,那么它是随机的吗,"更随机的"?
- @在循环的上下文中,Chrisnash系统时间不是随机的。为什么?因为它会随着时间的推移而改变,而骗子们通常都是该死的快!op得到相同序列的原因是因为默认的构造函数是随时间种子化的,而计算机的速度非常快,以系统精度衡量的时间在循环中不会发生可测量的变化。因此,使用时间连续地播种多个生成器是一个常见的错误。
- @但是使用不同的(较慢的)方法对多个随机数生成器实例进行种子设定是一个糟糕的解决方案。好的解决方案是重用随机数生成器的单个实例。
- @当然,但是很多其他人已经在其他答案和评论中指出了这一点,所以我只是在添加解释为什么OP的代码不起作用。(它还向Chris解释了为什么new Random()在这种情况下不是随机的,以及为什么。)这是重要的信息,在您确实有正当理由创建多个随机实例的情况下(例如,创建多个线程,每个线程都有自己的随机实例,在这种情况下,可能不希望有所有的读取获得相同的随机种子)。
- 如果您真的必须使用某种形式的随机性来生成种子,请考虑使用真正的随机数生成器,如random.org。否则(除非您有一些特殊的上下文,比如同时启动两个应用程序),新的random()应该可以做到这一点。另外,如果我没弄错的话,使用的种子使用pid和父级的pid(至少在.NET中是这样)。
- 如果你需要这样做,你会通过每次重新输入来引入大量额外的开销,并且可能想要更简单的东西。
不应在循环中创建新的Random实例。尝试以下方法:
1 2 3
| var rnd = new Random ();
for(int i = 0; i < 100; ++i )
Console .WriteLine(rnd .Next(1, 100)); |
单个Random实例生成的随机数序列应该是均匀分布的。通过为快速连续的每个随机数创建一个新的Random实例,您很可能会用相同的值对它们进行种子设定,并使它们生成相同的随机数。当然,在这种情况下,生成的序列将远离均匀分布。
为了完整起见,如果您确实需要重设一个Random,您将使用新种子创建一个新的Random实例:
1
| rnd = new Random (newSeed ); |
- 嗨,我对"种子"这个词有点困惑,它是如何工作的?它对java.util中的随机类有什么影响?
- @harigm:通常,(伪)随机数生成器是一种确定性算法,给定一个初始数(称为seed),生成一个足以满足统计随机性测试的数字序列。由于算法是确定性的,所以如果用相同的种子初始化,算法将始终生成完全相同的数字序列。这就是为什么系统时间(总是变化的东西)通常被用作随机数生成器的种子的原因。
- 当可预测性成为一个问题时,可能需要重新播种一个随机生成器(比如。在线赌博)。在这种情况下,除了使用更真实的随机生成器(如熔岩灯),您应该以比攻击者应用模式识别更快的速度重新设置,或者使用一个短寿命生成器池(并随机选择,确保没有两个生成器的种子具有相同的值,等等)。
位延迟,但系统使用的实现。random是Environment.TickCount:
1 2 3
| public Random()
: this(Environment.TickCount) {
} |
这就避免了长时间的强制执行DateTime.UtcNow.Ticks,这无论如何都是有风险的,因为它不代表系统启动后的计时周期,而是"自2001年1月1日午夜12:00:00(0:00:00 UTC,在公历中为0001年1月1日)以来的100纳秒间隔数"。
正在为种皮的StringFactory.GenerateRandomString寻找一个好的整数种子。
- 如果它是一个测试程序,只需使用数字4,它是确定性的,并且易于测试。
- @哈希表通常是个好主意,但并非所有测试都是单元测试。在系统/集成测试中,引入不太可预测的行为可以增加测试表面——如果您没有足够的资源来检查单个测试运行中的每个可能输入,那么这很有用。
如果由于某种原因您不能一次又一次地使用同一个Random,请尝试用一些随时变化的东西初始化它,比如时间本身。
1
| new Random (new System.DateTime().Millisecond).Next(); |
不过,记住这是一种不好的做法。
编辑:默认的构造函数已经从时钟中获取了它的种子,可能比我们更好。从msdn引用:
Random() : Initializes a new instance of the Random class, using a time-dependent default seed value.
下面的代码可能是您的最佳选择:
- 这个怎么样?new random(datetime.now.millisecond).next()获取当前毫秒。不过,我喜欢你的"用一些随时变化的东西初始化,比如时间本身"的想法。另外,如果我们在每次迭代中添加一个线程sleep(1),它将是真正随机的。
- 不要太快,这仍然是非常糟糕的随机性,只是有点不可预测。一个原因是我们的毫秒不会产生超过1000个值,因此1000个不同的随机序列,而不是您可能期望的2^31。
- 通过使用更大的不太可预测的数字(如new random((int)datetime.now.ticks),您将获得更好的随机性。
- 我相信默认种子是自系统启动时开始的滴答声。
- 如果随机类在同一毫秒内被初始化多次(就像在一个快速循环中一样),这将毫无帮助。DateTime.Now.Ticks的更新速度也不够快。
- 与new random()相同的问题,后者"使用与时间相关的默认种子值初始化随机类的新实例"。
- 这无法编译。
- 默认使用的seed并不好,我比较了它没有seed和毫秒作为seed,并且ms结果在调用过程中至少部分是随机的,只有毫秒的间隔,而在相同的情况下,无seed结果根本不是随机的。上面建议的guid one虽然总是呈现一个随机值,即使调用速度极快,所以它是最好的选择。
- @Christiandav&;n-在一个快速循环中,如果您不关心性能,那么按照omidoo的建议在循环中插入sleep()。否则,您可能需要将同一个实例保留一段时间,并且仅在可预测性成为问题时(比如每20次迭代左右)重新初始化它。这完全取决于你的需要
这对我很有用:
1 2 3 4 5
| private int GetaRandom ()
{
Thread .Sleep(1);
return new Random (DateTime .Now.Millisecond).Next();
} |
- 将当前线程置于休眠状态可能会导致并发性出现许多问题。您实际上锁定了正在使用的当前线程,我怀疑在大多数情况下,这将是您的主应用程序线程。为了实现这一点,我建议将RNG隔离到它自己的线程中,这样就可以认为它是线程安全的,并异步地使用它。
1
| public static Random rand = new Random (); // this happens once, and will be great at preventing duplicates |
注意,这不用于加密目的。
- 请注意,与Java不同,.NET中的EDCOX1 0Ω不是线程安全的。在不同线程上调用没有适当锁定机制的Next可能会导致随机数生成器的内部状态损坏。
- @梅尔达:或者更糟;我已经让它抛出了例外。
- @杰森:在大多数情况下,获得一个例外比处理糟糕的结果要好。我宁愿让我的在线扑克应用程序崩溃,也不愿轻易预测。
- @在这种情况下,你应该选择加密随机
- 操作人员没有提到线程安全。打电话很快。
一个好的种子初始化可以这样做
1
| Random rnd = new Random ((int)DateTime .Now.Ticks); |
勾号将是唯一的,转换成一个可能有零值的int就可以了。
我在大多数情况下都使用这个,如果需要重复序列,就保留种子。
1 2
| var seed = (int) DateTime .Now.Ticks;
var random = new Random (seed ); |
或
1
| var random = new Random ((int)DateTime .Now.Ticks); |