两个不同的种子生成相同的“随机”序列

21
也许有一个非常逻辑的解释,但我似乎无法理解为什么使用.NET的Random Class (System)时,种子02,147,483,647会产生相同的“随机”序列。
快速代码示例:
var random1 = new Random(0);
var random2 = new Random(1);
var random3 = new Random(int.MaxValue); //2,147,483,647

var buffer1 = new byte[8];
var buffer2 = new byte[8];
var buffer3 = new byte[8];

random1.NextBytes(buffer1);
random2.NextBytes(buffer2);
random3.NextBytes(buffer3);

for (int i = 0; i < 8; i++)
{
    Console.WriteLine("{0}\t\t{1}\t\t{2}", buffer1[i], buffer2[i], buffer3[i]);
}

输出:

26      70      26
12      208     12
70      134     76
111     130     111
93      64      93
117     151     115
228     228     228
216     163     216

您可以看到,第一和第三个序列是相同的。有人能解释一下这个吗?

编辑:正如alro指出的那样,这些序列并不相同。但它们非常相似。


3
System.Random在许多方面都有设计缺陷,这是其中之一。 - CodesInChaos
3
非常有趣的观察!加一! - quetzalcoatl
2
@RuudLenders 我了解了。这非常奇怪。显然,那个函数并不完美。 :( 微软,你怎么能这样! - Christopher Bales
15
有没有人会指出他的结果其实是不一样的?第三个和第六个数字是不同的。 - JustAPoring
3
@alro 没问题,因为他们所讨论的功能是生成预先计算的"种子数组"。然后使用该数组来生成每个样本。如果种子在数组中重复出现的次数越多,那么随机数序列就会越接近(在这种情况下,数组仅相差一个项目)。但是...两个序列不应该重复相同的数字模式... - Adriano Repetti
显示剩余3条评论
1个回答

10

嗯,原因将与Random类使用的派生函数连接到伪随机序列从种子中获得。因此,真正的答案是数学问题(并且超出了我的能力范围)。

事实上,我不相信有任何保证两个不同的种子会产生不同的序列。

编辑 好的-我要做bitbonk所做的事情-但解释一下 为什么

public Random(int Seed)
{
    int num = (Seed == -2147483648) ? 2147483647 : Math.Abs(Seed);
    int num2 = 161803398 - num;
    this.SeedArray[55] = num2;
    int num3 = 1;
    for (int i = 1; i < 55; i++)
    {
        int num4 = 21 * i % 55;
        this.SeedArray[num4] = num3;
        num3 = num2 - num3;
        if (num3 < 0)
        {
            num3 += 2147483647;
        }
        num2 = this.SeedArray[num4];
    }
    for (int j = 1; j < 5; j++)
    {
        for (int k = 1; k < 56; k++)
        {
            this.SeedArray[k] -= this.SeedArray[1 + (k + 30) % 55];
            if (this.SeedArray[k] < 0)
            {
                this.SeedArray[k] += 2147483647;
            }
        }
    }
    this.inext = 0;
    this.inextp = 21;
    Seed = 1;
} 

我们实际上不需要深入代码就能看出原因-从上到下阅读代码时,当种子为02147483647时,以上代码将存储哪些值:

int num = (Seed == -2147483648) ? 2147483647 : Math.Abs(Seed);
  =>  num is 0 and 2147483647

int num2 = 161803398 - num;
  => num2 is 161803398 and -1985680249

this.SeedArray[55] = num2;
  => this.SeedArray is as above in both cases

int num3 = 1;
for (int i = 1; i < 55; i++)
{
  int num4 = 21 * i % 55
  this.SeedArray[num4] = num3;

  => num4 is 21, SeedArray[21] is 1

num3 = num2 - num3
  => num3 is 161803397 and -1985680250

if(num3 < 0)
  num3 += 2147483647

  => num3 is 161803397 and 161803397

仅经过第一次循环,算法已经收敛于两个种子值。

编辑

正如问题所指出的那样 - 这些序列并不相同 - 但它们显然非常相似 - 这里我们可以看到相似性的原因。


1
感谢提供详细信息!值得注意的是,{0,int.Max,int.Min}会产生相同的种子,正如这个线程所证明的那样。我想知道这个实现是否还有其他类似的共同种子,如果有更多的话,那将为一篇文章提供很好的基础:) - quetzalcoatl
+1 干得好!@quetzalcoatl 嗯,我认为 System.Random 实现并不打算成为一个像样的随机数生成器,而是一个准备好使用、非常快速的例程,每次使用时甚至无需考虑其好坏。这就是为什么(即使在框架内部)有那么多不同的实现方式。 - Adriano Repetti
4
@ReacherGilt: 我认为说他不值得得到那么好的回答是不公平的。他提出了一个好问题,针对他来说可能是黑盒代码。我不知道那本书,作为一个不用C语言编程的人,可能不会去找。你说的找答案的方式是正确的,但这仍然是一个好问题,并且得到了一个好回答。 - Chris
2
@ReacherGilt:我不确定我看到的答案是否形式良好。当我看到它时,它只是上面构造函数的复制和粘贴代码,没有任何实际的答案与之配合。但我并不是说没有好的答案。我只是不确定说这超出了应得的范围是否公平。如果是这样的话,那就意味着这是一个糟糕的问题,而评分(16个赞,写作时没有踩)表明这远非如此。 - Chris
说得好。我想我是从这样一个角度来看待问题的,一旦你拥有了构造函数的源代码,你就知道正在发生什么,并且可以打开这个黑匣子来测试输入并检查函数的内部工作原理。 - Reacher Gilt
显示剩余4条评论

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