为什么Random.Next()总是返回相同的数字

24

请考虑以下方法:

private static int GenerateRandomNumber(int seed, int max)
{
   return new Random(seed).Next(max);
}

在我的机器上,执行这个循环通过1500次迭代得到相同的数字:

  for (int i = 0; i < 1501; i++)
            {
                int random = GenerateRandomNumber(100000000, 999999999);
                Console.WriteLine(random.ToString());
                Console.ReadKey();
            }

每次迭代我得到的是145156561。

我没有紧急的问题,只是因为.Next(max)说“返回小于指定最大值的非负随机数”,所以我对这种行为很好奇。也许我没有理解某些基本的东西。


7
“Seed” 的意思就是这样。 - SLaks
1
我现在明白了。我只是认为它在通过Intellisense展示的方式上不够明显。 - Ta01
7个回答

48

你总是使用相同的种子来初始化一个新的实例,然后获取前面的最大值。通过使用种子,你可以确保获得相同的结果。

如果你想要一个静态的、随机数生成器生成不同的结果,你应该稍微修改一下代码。然而,由于 Random 不是线程安全的,当静态使用时,它需要进行一些同步操作。例如:

private static Random random;
private static object syncObj = new object();
private static void InitRandomNumber(int seed)
{
     random = new Random(seed);
}
private static int GenerateRandomNumber(int max)
{
     lock(syncObj)
     {
         if (random == null)
             random = new Random(); // Or exception...
         return random.Next(max);
     }
}

3
如果你需要大量的随机数,这可能会导致电脑崩溃 :-) 至少在使用C# 4.0以下版本时,请使用SpinLock或将Random对象设为线程静态。 - xanatos
你应该使用双重检查锁定。 - SLaks
1
@SLaks:并没有真正帮助——Random.Next()不是线程安全的,所以你总是需要一个锁。这里的锁不是为了懒惰实例化而存在... - Reed Copsey
@xanatos:真的-如果您从许多线程中使用此功能,则不建议使用此功能。在这种情况下,ThreadLocal <Random>可能是更好的选择,但除非必要,否则我会避免使用它,因为它有其自己独特的问题。 - Reed Copsey
6
作为一个免费的额外奖励,我将提供Jon Skeet关于Random和ThreadLocal的文章!:-) http://csharpindepth.com/Articles/Chapter12/Random.aspx - xanatos
显示剩余3条评论

9

7
SO里真的没有幽默的空间吗? - dbrin
1
虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,则仅有链接的答案可能会失效。- 来自审核 - jv42
尊敬的@jv42,恕我直言,我无法复制漫画内容,因此在这种情况下,链接是合理的。 - dbrin
你不能这样做,而且这也可能没有什么用。你在这里发布的答案应该包含有助于解决问题的有用内容。原始问题中存在一些代码错误,用于生成随机数,而不是关于 RNG 理论的问题。 - jv42
虽然我欣赏幽默,特别是迪尔伯特,但我认为你的“答案”并没有帮助。 - jv42

6
问题在于每次都使用相同的种子数创建新的Random实例。应该只创建一个Random实例(必要时将其存储在静态变量中),并简单地在该实例上调用next方法。
随机数生成并不是真正的随机,详见维基百科

4

大家好, 这也困扰了我很久,但答案很简单。在生成随机数之前改变种子。

例如: 我想要生成1到10之间的随机数。

Random rnd = new Random(DateTime.Now.Second);
int random_number = rnd.Next(10);

把它放在一个循环里并运行三次。它会输出小于10的随机数。

对于那些偶然看到这篇文章的人,需要说明的是,如果迭代次数足够大,这种方法将不起作用,因为随机种子不会是唯一的。 - C O

3

如果你不确定内部工作原理,可以查看维基百科进行了解,但这很简单。

public class MathCalculations
{
    private Random rnd = new Random();

    public Int32 getRandom(Int32 iMin, Int32 iMax)
    {
        return rnd.Next(iMin, iMax);
    }
}

public class Main
{
    MathCalculations mathCalculations = new MathCalculations();
    for (int i = 0; i < 6; i++)
    {
        getRandom(0,1000);
    }
}

将生成Number1、Number2、Number3、Number4、Number5和Number6(1个种子,1个包含许多数字序列的序列,随机*不是真正的随机,但是近似*)

但如果你这样做:

public class MathCalculations
{
    public Int32 getRandom(Int32 iMin, Int32 iMax)
    {  
        Random rnd = new Random();
        return rnd.Next(iMin, iMax);
    }
}

public class Main
{
    MathCalculations mathCalculations = new MathCalculations();
    for (int i = 0; i < 6; i++)
    {
        getRandom(0,1000);
    }
}

您现在会得到Number1,Number1,Number1,Number1,Number1,Number1(1个种子,6个相等的数字序列,每个相等的序列始终选择相同的起始数字)。 在某些时候,Number1将不同,因为种子随时间而变化。 但是您需要等一段时间才能完成这项工作,尽管如此,您永远不会从序列中选择number2。
原因是每次使用相同的种子生成新序列,因此序列一遍又一遍地重复,每次随机生成器都会选择其序列中的第一个数字,而在相同的种子下,它当然始终相同。 不确定这是否符合随机生成器的基本方法,但它的行为就是这样。

3
伪随机数生成器通常通过选择种子来工作,然后基于该种子生成确定性序列。每次选择相同的种子,都会生成相同的序列。
在.NET中,只有“仅”2^32个不同的随机序列。

1

如果有人正在寻找一个"快速而简单"的"解决方案"(我使用这个术语要谨慎),那么对于大多数人来说,这将足够。

int secondsSinceMidnight = Convert.ToInt32(DateTime.Now.Subtract(DateTime.Today).TotalSeconds);
Random rand = new Random(secondsSinceMidnight); 
var usuallyRandomId = rand.Next();

请注意我使用的通常随机。我同意标记为答案的项目是更正确的方法。


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