在C#中种植伪随机数生成器

16
我需要一个C#Random类实例的种子,我读到大多数人使用当前时间的滴答计数器作为种子。但那是一个64位值,而种子需要是32位值。我认为GetHashCode()方法应该为其对象提供一个合理分布的值,因此可以使用它来避免仅使用滴答计数器的低32位。然而,我找不到Int64数据类型的GetHashCode()的任何信息。
所以,我知道这并不重要,但以下代码是否和我想的一样好用(我无法进行随机性试错),或者可能与使用(int)DateTime.Now.Ticks相同?或者甚至更糟糕?谁可以给出一些启示。
int seed = unchecked(DateTime.Now.Ticks.GetHashCode());
Random r = new Random(seed);

编辑:为什么我需要一个种子,而不是让Random()构造函数来完成工作?我需要将种子发送给使用相同种子生成相同随机序列的其他客户端。


2
你忘记写下你需要一个种子的真正好的理由了。 - Claus Jørgensen
2
好的,我现在添加了那个“非常好的参数”。 :) - Daniel A.A. Pelsmaeker
3个回答

34

new Random()已经使用了当前时间,等价于new Random(Environment.TickCount)

但这是实现细节,可能会在未来版本的.net中发生变化。

我建议使用new Random(),只有当你想要获得可重现的伪随机数序列时才提供固定的种子。

由于需要一个已知的种子,所以只需像MS一样使用Environment.TickCount,然后将其作为种子传递给其他程序实例。

如果您在短时间内创建多个Random实例(可能是16ms),它们可以被种植到相同的值,从而创建相同的伪随机序列。但这在这里很可能不是问题。这个常见的陷阱是由于Windows仅每几毫秒更新当前时间(DateTime.Now/.UtcNow)和TickCount(Environment.TickCount)。确切的间隔取决于Windows的版本以及正在运行的其他程序。它们不改变的典型间隔为16ms或1ms。


2
提到自己提供种子值的唯一时机是当您想要重现特定值序列的情况下。类似于Windows纸牌游戏中的“游戏号”选项。 - Andrew Barber
@AndrewBarber 如果您在生成随机数的过程之外迭代方法/块,则需要唯一种子,并且希望避免由于时间分辨率不足而导致的碰撞风险,就像CodesInChaos所写的那样。 - Alex
@Alex,我相当确定CodeInChaos并不是这个意思。我引用他的话,“只有在您想要获得可重现的...值序列时才提供固定种子”。您不应该创建太多的Random实例。 - Andrew Barber
@Alex 1) 对于这些,你不应该创建单独的Random实例。2) 在需要任何安全性的情况下,您永远不应该使用Random。 - Andrew Barber
1
@Alex 我也是这么想的 ;) 我认为你对此有所了解,但我认为我们之间存在一些微小的技术细节或其他问题!可怜的CodeInChaos会想知道他的答案为什么会引起如此大的反响! ;) - Andrew Barber
显示剩余9条评论

31
如果您需要使用除当前时间(在这种情况下,可以使用默认构造函数)以外的东西进行初始化,可以使用以下代码:

如果你需要用其他东西来初始化它,而不是当前时间(在这种情况下,你可以使用默认构造函数),你可以使用以下代码:

Random random = new Random(Guid.NewGuid().GetHashCode());

3
为什么这比 DateTime.Now.Ticks.GetHashCode() 更有价值呢? - Daniel A.A. Pelsmaeker
17
这种方法更好,因为它不会受到DateTime.Now每几毫秒才更改一次的问题的影响。使用这种方法时,即使在快速初始化的情况下,两个随机实例获得相同的种子的可能性也非常小。 - CodesInChaos
1
你确定没有哈希冲突吗? - oɔɯǝɹ
1
@CodesInChaos 正如 oɔɯǝɹ 所说,如果只使用 GUID,可能会减少重复性。https://dev59.com/kWw05IYBdhLWcg3wSABH - bbill
@oɔɯǝɹ 很不幸,哈希冲突将会很常见。32位种子太小了,无法确保良好的播种。 - CodesInChaos
1
那种方法会在gethashcode由于随机数对种子进行abs操作并且abs在int.minvalue上爆炸时随机地崩溃。你需要这样做... `Random random = new Random(Guid.NewGuid().GetHashCode() & int.MaxValue);`以确保您只发送正整数。 - Ramon Leon

0

我有一个类似的问题,需要从一个较大的问题列表中选择一组随机问题。但是当我使用时间作为种子时,它会给出相同的随机数。

所以这是我的解决方案。

    int TOTALQ = 7;
    int NOOFQ = 5;

    int[] selectedQuestion = new int[TOTALQ];

    int[] askQuestion = new int[NOOFQ];

    /*   Genarae a random number 1 to TOTALQ
     *   - if that number in selectedQuestion array is not o
     *   -     Fill askQuestion array with that number
     *   -     remove that number from selectedQuestion
     *   - if not re-do that - - while - array is not full.    
     */

    for (int i = 0; i < TOTALQ; i++)  // fill the array
        selectedQuestion[i] = 1;

    int question = 0;

    int seed = 1;

    while (question < NOOFQ)
    {       
        DateTime now1 = new DateTime();
        now1 = DateTime.Now;    
        Random rand = new Random(seed+now1.Millisecond);
         int RandomQuestion = rand.Next(1, TOTALQ);

         Response.Write("<br/> seed  " + seed + " Random number " + RandomQuestion );



        if (selectedQuestion[RandomQuestion] != 0)      
        {
            selectedQuestion[RandomQuestion] = 0;  // set that q =0 so not to select           
            askQuestion[question] = selectedQuestion[RandomQuestion];
            Response.Write(".  Question no " + question + " will be question " + RandomQuestion + " from list " );
            question++;
        }

        seed++;         

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接