1

Yesterday I wrote my first answer at Programming Puzzles & Code Golf. The question said this:

Given an input string S, print S followed by a non-empty separator in the following way:

  • Step 1: S has a 1/2 chance of being printed, and a 1/2 chance for the program to terminate.

  • Step 2: S has a 2/3 chance of being printed, and a 1/3 chance for the program to terminate.

  • Step 3: S has a 3/4 chance of being printed, and a 1/4 chance for the program to terminate.

  • Step n: S has a n/(n+1) chance of being printed, and a 1/(n+1) chance for the program to terminate.

So I went and wrote this code (ungolfed):

Action<string> g = s =>
{
    var r = new Random();
    for (var i = 2; r.Next(i++) > 0;)
        Console.Write(s + " ");
};

This code works fine, but then someone said that I could save a few bytes creating the r variable inline, like this:

Action<string> g = s =>
{
    for (var i = 2; new Random().Next(i++) > 0;)
        Console.Write(s + " ");
};

I tried but when I executed the code, it always went in one of two possibilities:

  • Either the program halted before printing anything (the first call to Next() returns 0), or
  • The program never stops (the calls to Next() never return 0).

When I reverted the code to my original proposal, the program stopped more randomly as expected by the OP.

I know that the new Random() constructor depends on time, but how much? If I add a Sleep() call, the code behaviour starts to seem really random (but not much, the strings returned are still longer than the ones returned by the initial code):

Action<string> g = s =>
{
    for (var i = 2; new Random().Next(i++) > 0; Thread.Sleep(1))
        Console.Write(s + " ");
};

If I increment the sleep time to 10 ms, now the code really behaves like the original one.

So why is this? How much does the Random class depends on time? How exactly does the Random class seeds the number generator when calling the empty constructor?


Note: I know that creating a single Random object is the best practice, I just wanted to know a bit more than what the MSDN says:

The default seed value is derived from the system clock and has finite resolution.

What is that "finite resolution" the Random class default constructor uses as seed? How much time should we separate the construction of two Random objects to get different sequences? How much would those two different sequences differ when creating the Random instances too close in time?

Charlie
  • 624
  • 1
  • 8
  • 22
  • 4
    You should create one instance of your Random class and use that throughout your code. Don't recreate it every time. – rene Jun 13 '17 at 08:33
  • 2
    Never ever do `new Random().Next(i++)`, but create one instance of `Random` for all. – Dmitry Bychenko Jun 13 '17 at 08:35
  • https://stackoverflow.com/a/24547238/5528593 – René Vogt Jun 13 '17 at 08:35
  • 1
    and https://stackoverflow.com/q/767999/579895 – Pikoh Jun 13 '17 at 08:37
  • some one said you could save a few bytes by in-lining `new Random()`? Wrong! you actually waste a lot of bytes in memory if you do that ;) – M.kazem Akhgary Jun 13 '17 at 08:38
  • 2
    For code golf, it's the size of the source code that matters. – Lasse V. Karlsen Jun 13 '17 at 08:38
  • And thats why C# is a bad choice for code golf - because it is very "narrative" and less "cryptic" than others like the ones which solves this puzzle with "[NÌL.R#," – Sir Rufo Jun 13 '17 at 08:43
  • 1
    @SirRufo and even so, the answer in C# was the most upvoted. Obviously C# cannot compete with other languages, but C# users can compete between themselves. – Charlie Jun 13 '17 at 08:46
  • @CarlosAlejo C# is not designed to write the shortest (and very "cryptic") source code (that will need a lot of documentation) - that is one reason why I have choosen this language. – Sir Rufo Jun 13 '17 at 08:51
  • 3
    Have a look at the source http://referencesource.microsoft.com/#mscorlib/system/random.cs – Sir Rufo Jun 13 '17 at 08:56
  • 1
    Others can reopen the question if they want but I feel the linked question and answer is the only correct one. If you ask "how finite is that time resolution" then there is no right answer, it depends on many factors but chiefly, **this is undocumented behavior**. That is, the *behavior* is documented, but the actual resolution is undocumented. On most Windows systems I think this resolution is about 15ms but again, **this is undocumented**. As such, there is no way to answer this . – Lasse V. Karlsen Jun 13 '17 at 09:11
  • It uses Environment.TickCount to generate the seed. Can't change more frequently than once every millisecond by design, in practice it changes no more often than 64 times per second. Runs off the OS clock interrupt, also the one that ends a Thread.Sleep(). So sleeping for more than 0 msec always ensures you get another seed, actual interrupt rate and sleep amount does not matter. – Hans Passant Jun 13 '17 at 17:41

1 Answers1

0

What is that "finite resolution" the Random class default constructor uses as seed?

It uses Environment.TickCount which has a resolution of one millisecond.

How much time should we separate the construction of two Random objects to get different sequences?

As per the previous section, by at least one millisecond - or manually feed another seed to the constructor each time (or, well, reuse the same generator?)

How much would those two different sequences differ when creating the Random instances too close in time?

Matt Moss did a nice visualization in his Random Headaches from System.Random blog post:

Each row of the bitmap represents five first generated numbers with that seed (without preserving generated order):

Random() sets with adjacent seeds, image by Matt Moss

As you can see, numbers are being selected from distinct but related sequences. As MSDN on Random says, "The chosen numbers... are sufficiently random for practical purposes."

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152