16

I need a seed for an instance of C#'s Random class, and I read that most people use the current time's ticks counter for this. But that is a 64-bit value and the seed needs to be a 32-bit value. Now I thought that the GetHashCode() method, which returns an int, should provide a reasonably distributed value for its object and this may be used to avoid using only the lower 32-bits of the tick count. However, I couldn't find anything about the GetHashCode() of the Int64 datatype.

So, I know that it will not matter much, but will the following work as good as I think (I can't trial-and-error randomness), or maybe it works the same as using (int)DateTime.Now.Ticks as the seed? Or maybe it even works worse? Who can shed some light on this.

int seed = unchecked(DateTime.Now.Ticks.GetHashCode());
Random r = new Random(seed);

Edit: Why I need a seed and don't just let the Random() constructor do the work? I need to send the seed to other clients which use the same seed for the same random sequence.

Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157

3 Answers3

34

new Random() already uses the current time. It is equivalent to new Random(Environment.TickCount).

But this is an implementation detail and might change in future versions of .net

I'd recommend using new Random() and only provide a fixed seed if you want to get a reproducible sequence of pseudo random values.

Since you need a known seed just use Environment.TickCount just like MS does. And then transmit it to the other program instances as seed.

If you create multiple instances of Random in a short interval (could be 16ms) they can be seeded to the same value, and thus create the same pseudo-random sequence. But that's most likely not a problem here. This common pitfall is caused by windows updating the current time(DateTime.Now/.UtcNow) and the TickCount(Environment.TickCount) only every few milliseconds. The exact interval depends on the version of windows and on what other programs are running. Typical intervals where they don't change are 16ms or 1ms.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • 2
    +1 for mentioning that the only time to really provide your own seed value is when you want to reproduce a specific sequence of values. Think along the lines of the Windows Solitaire "game number" option. – Andrew Barber Oct 30 '10 at 23:25
  • I edited my question. I know about the `new Random()` constructor, but I need the seed. – Daniel A.A. Pelsmaeker Oct 30 '10 at 23:30
  • @AndrewBarber Or if you iterate the method/block outside the generation of the Random, need unique seeds and want to avoid the risk of collisions due to insufficient time resolution, just as CodesInChaos writes. – Alex Jun 05 '14 at 03:25
  • @Alex I'm fairly sure that's actually not what CodeInChaos is saying. I quote, "only provide a fixed seed if you want to get a reproducible sequence of...values". You *shouldn't* be creating so many Random instances. – Andrew Barber Jun 05 '14 at 03:30
  • @AndrewBarber True, you should not keep the instantiation of a Random within a loop. But you could have a method that initiates an internal Random object in order to perform it's task, and this method might not have been explicitly designed to be used in a loop, but might in some circumstances very well used that way by the developers. – Alex Jun 05 '14 at 04:48
  • @Alex you shouldn't try to fix a bad design by piling another bad design on top. If what you describe can happen, the method declaring the Random (or the whole object containing the method) probably smells all around. – Andrew Barber Jun 05 '14 at 04:51
  • @AndrewBarber Well, I see it this way: The initiation of a Random is designed to be seeded differently each time, it's implemented by using the system clock, and do to insufficient resolution this is prone to collisions if called to fast. If you think that this vulnerability poses a risk, you can choose to employ external infrastructure to handle the management of the implementation symptoms, or you can do it as close to the source as possible. If I have a choice, I'd rather contain the weakness close to the source, instead of building external infrastructure around it. If possible. – Alex Jun 05 '14 at 05:08
  • @Alex yes. Contain it close to the source... don't expose a possibility of constructing multiple Randoms in quick succession at all, no matter what calls you method, however often. But all this begs the question of what you would even pass in to the constructor in the first place... not the time; there's no Random you can use... see my point? You've trapped yourself in a circle. Only solution: prevent multiple Randoms like this all. – Andrew Barber Jun 05 '14 at 05:14
  • @AndrewBarber But if it worked as intended, I would not have to care about investigating the system for the risk that some initiations of a Random might be called to fast in some possible chain of events. That's a significant overhead that's worth avoiding if you can. I'm not saying that you always can, or that it always will be worth it. One simple example could be this: GenerateAuthToken(username), where we can incorporate the username in the seed to easily make sure that it's highly unlikely that two different users will get the same token, even if called subsequently. – Alex Jun 05 '14 at 05:31
  • @Alex 1) you should not be creating individual Random instances for those. 2) you should never use Random for anything needing to be in any way secure. – Andrew Barber Jun 05 '14 at 05:34
  • @AndrewBarber Let's try to round of this discussion, but yes, in this isolated case I should for example use a static Random in the class. Or I might even keep a specific class for keeping a static Random. But the interesting feature of the example is that the randomness is most important as a projection together with other input values, that I implied authentication was a bad choice from my side. But yes, for most cases I think you're more right and that I might have overestimated the cases where seeding the Random manually is a valuable option. But is not _never_ except reproduction. – Alex Jun 05 '14 at 05:44
  • 1
    @Alex I was thinking the same thing ;) I do think you have a grasp on this, but I just think there's some tiny technicality or something that's dodging around between us! Poor CodeInChaos is gonna wonder why his answer blew up! ;) – Andrew Barber Jun 05 '14 at 05:49
  • @AndrewBarber This is a pretty old answer, and I no longer fully agree with it. In my opinion even one instance of `Random` is too much. It can't be seeded properly and it generates biased output. Just throw it away and use a proper PRNG. If I really had to use `Random`, I'd add a thread-safe counter initialized to a random value as seed for thread-local instances of `Random`. – CodesInChaos Jun 05 '14 at 07:02
  • @code yeah, it's really only worthwhile for trivial stuff, and that's otherwise a decent solution. – Andrew Barber Jun 05 '14 at 10:54
31

If you need to seed it with something other than the current time (in which case you can use the default constructor), you can use this:

Random random = new Random(Guid.NewGuid().GetHashCode());
steinar
  • 9,383
  • 1
  • 23
  • 37
  • 3
    And why would that be a better value than `DateTime.Now.Ticks.GetHashCode()`? – Daniel A.A. Pelsmaeker Oct 30 '10 at 23:31
  • 17
    It's better because it doesn't suffer from the problem that `DateTime.Now` only changes every few milliseconds. With this method it's very unlikely that two instances of random get the same seed even when initialized in quick succession. – CodesInChaos Jun 13 '11 at 12:10
  • 1
    And you're certain that there are no hash-collisions? – oɔɯǝɹ Apr 17 '13 at 15:17
  • 1
    @CodesInChaos Exactly what oɔɯǝɹ says - there probably would be less if you just used the GUIDs. http://stackoverflow.com/questions/7326593/guid-gethashcode-uniqueness – bbill Jun 24 '13 at 21:46
  • @oɔɯǝɹ Unfortunately hash collisions will be common. A 32 bit seed is simply too small to ensure good seeding. – CodesInChaos Jun 05 '14 at 06:56
  • 1
    That approach will randomly blow up any time gethashcode happens to return int.minvalue due to random doing an abs on the seed and abs blowing up on int.minvalue. You need to do this instead... `Random random = new Random(Guid.NewGuid().GetHashCode() & int.MaxValue);` To ensure you're only sending in positive integers. – Ramon Leon Mar 28 '17 at 23:08
0

I had a similar question , to select a random set of questions from a larger list of questions. But when I use the time as the seed it gives the same random number .

So here is my solution.

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

    }
Saman
  • 1
  • 2