21

Maybe there is a very logic explanation for this, but I just can't seem to understand why the seeds 0 and 2,147,483,647 produce the same "random" sequence, using .NET's Random Class (System).

Quick code example:

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]);
}

Output:

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

As you can see, the first and the third sequence are the same. Can someone please explain this to me?

EDIT: Apparently, as alro pointed out, these sequences are not the same. But they are very similar.

Rudey
  • 4,717
  • 4
  • 42
  • 84
  • 1
    Have you tried any other arbitrary number to seed against, and if so, are you getting the same results with that number? – Christopher Bales Nov 13 '12 at 16:19
  • 3
    `System.Random` is broken-by-design in many ways. This is one of them. – CodesInChaos Nov 13 '12 at 16:27
  • 3
    +1 for very interesting observation! – quetzalcoatl Nov 13 '12 at 16:28
  • 1
    @ChristopherBales I've added a third seed to show that my code example is not broken. – Rudey Nov 13 '12 at 16:35
  • 2
    @RuudLenders I see. This is very strange. Apparently that function isn't perfect. :( Microsoft, how could you! – Christopher Bales Nov 13 '12 at 16:36
  • 14
    Is no one going to point out that his results are **NOT** the same? The third and sixth numbers are different. – JustAPoring Nov 13 '12 at 16:49
  • 3
    @alro **it's OK** because function they're talking about is to generate a pre-calculated _seed array_. It's then used to generate each sample. More seeds are the same in that array and more you'll get close sequences of random numbers (in this case arrays differs by one item only) but...two sequences shouldn't repeat the same numeric pattern... – Adriano Repetti Nov 13 '12 at 16:53
  • 1
    That's just so weird. You may use [FastRandom](http://www.codeproject.com/Articles/9187/A-fast-equivalent-for-System-Random) that uses one of the best algorithms (xorshift) right now. Not only its faster, its safe from the weirdness you just faced – nawfal Nov 23 '12 at 23:34

1 Answers1

10

Well, the reason will be connected with whatever derivation function is used by the Random class to derive a pseudo-random sequence from the seed. The real answer, therefore, is mathematical (and beyond my ability).

Indeed - I don't believe there's any guarantee that two different seeds will necessarily produce different sequences anyway.

Edit Okay - I'm going to do what bitbonk has done - but explain why:

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;
} 

We don't actually need to go too far into the code to see why - reading the code from top to bottom these are the values that will be stored by the above code when the seed is 0 and when the seed is 2147483647:

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

After just the very first loop, algorithm has already converged for the two seed values.

Edit

As has been pointed out on the question - the sequences aren't the same - but they are clearly very very similar - and here we can see the reason for that similarity.

Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160
  • 1
    Thanks for the details! Notably {0,int.Max,int.Min} result in the same seed, as this thread proves. I wonder if, and how much this implementation has other such co-seeds? If there are more of them, that would form a nice base for an article:) – quetzalcoatl Nov 13 '12 at 16:45
  • +1 Nice job! @quetzalcoatl well I think System.Random implementation isn't intended to be even a decent random numbers generator but a ready to use, very quick routine to use every time you do not even have to think about its goodness. That's why (even inside the framework) there are so many different implementations of that. – Adriano Repetti Nov 13 '12 at 16:49
  • @Adriano But the documentation implies more. http://msdn.microsoft.com/en-us/library/ctssatww.aspx If your application requires different random number sequences, invoke this constructor repeatedly with different seed values. – paparazzo Nov 13 '12 at 16:51
  • This answer is far more than the asker deserved. The code that @bitbonk posted looks like a rng from Numerical Recipies in C, which is a pretty foundational computer science text. You could read over [http://nuclear.fis.ucm.es/COMP-PHYS/RANDOM/RandomNumbers.pdf](chapter 7) and find the answer about algorithmical weaknesses for yourself. Hint: What do the two seeds produce for SeedArrays above? Why? – Reacher Gilt Nov 13 '12 at 16:58
  • 4
    @ReacherGilt: I think its unfair to say that he didn't deserve an answer that good. He asked a good question on what was to him presumably a black box piece of code. I don't know that book and as somebody who doesn't program in C probably wouldn't think to pick it up. You are right about the way to find the answer but it is still a good question with a good answer. – Chris Nov 13 '12 at 17:01
  • @Blam well from that I understand only to do not use the same seed twice if I need different sequences. They talk about algorithm goodness (and give references in remarks section of class' overview) and they say it's good for "practical purposes" but _To generate a cryptographically secure random number_... they point to something else. – Adriano Repetti Nov 13 '12 at 17:01
  • @Chris it's actually a great answer! But bitbonk's now deleted answer was "good" enough to provide a definitive reason for the asker and he wound up getting grief for it. – Reacher Gilt Nov 13 '12 at 17:06
  • 2
    @ReacherGilt: I dunno if I saw that answer in a good form. When I saw it it was just copy and paste code of the constructor above without any actual answer to go with it. But I'm not saying that there can't be any good answers. I am just not sure that its fair to say that its more than was deserved. If it was that would imply a bad question and the rating (16 up, no down at time of writing) suggests that is far from the case. – Chris Nov 13 '12 at 17:09
  • Fair point. I guess I was coming from a point where once you have the source code for the ctor, you know exactly what's going on and can pry open that black box to test inputs and examine the function's inner workings. – Reacher Gilt Nov 13 '12 at 17:14