Random Number in C#, Be careful of some of the samples you find.

I recently came across this code on the internet for generating a random number with C#.

   1: private static int RandomNumber(int min, int max)
   2: {
   3:    Random random = new Random();
   4:    return random.Next(min, max);
   5: }

The problem with this code is that for a novice developer (and maybe even some advance developers) it can be misleading and in some instance will not work.  Take for instance the following code…

 

The issue with this is not immediately obvious, but if you remember one thing about the Random object in C# is that when you do not pass it any parameters is uses a time based seed.  In addition, if you use the same seed for an new instance the same numbers will be generated, thus they number really are not random. The same is also true for the Random.Next function, if called within the same time the same number as previous will be generated.  So in the case of the function above, the for loop can go through the loop 4-5 times within a millisecond, so that means the same “random” number will be generated.  So this function really does not generate a real random ID (since the same characters are repeated 4-5 times).

   1: public static string GenerateID(int MinSize, int MaxSize)
   2: {
   3:    string stRefID = String.Empty;
   4:    int iChosenMaxSize = RandomNumber(MinSize, MaxSize);
   5:  
   6:    for (int x = 1; x <= iChosenMaxSize; x++)
   7:    {
   8:       int iCharType = RandomNumber(1, 3);
   9:  
  10:       switch (iCharType) 
  11:       {
  12:          case 1:
  13:          {
  14:             stRefID += char.ConvertFromUtf32(RandomNumber(48, 57));
  15:             break;
  16:          }
  17:          case 2:
  18:          {
  19:             stRefID += char.ConvertFromUtf32(RandomNumber(65, 90));
  20:             break;
  21:          }
  22:          case 3:
  23:          {
  24:             stRefID += char.ConvertFromUtf32(RandomNumber(97, 122));
  25:             break;
  26:          }
  27:       }
  28:    }
  29:  
  30:    return stRefID; 
  31: }

So the code that does what is really needed cannot use the RandomNumber function that I found on the internet.  Below is how the the function above can be written not using the RandomNumber function to do what is expected.

   1: public static string GenerateID(int MinSize, int MaxSize)
   2: {
   3:    string stRefID = String.Empty;
   4:    Random random = new Random();
   5:    int iChosenMaxSize = random.Next(MinSize, MaxSize); 
   6:  
   7:    for (int x = 1; x <= iChosenMaxSize; x++)
   8:    {
   9:       int iCharType = random.Next(1, 3);
  10:  
  11:       switch (iCharType)
  12:       {
  13:          case 1:
  14:          {
  15:             stRefID += char.ConvertFromUtf32(random.Next(48, 57));
  16:             break;
  17:          }
  18:          case 2:
  19:          {
  20:             stRefID += char.ConvertFromUtf32(random.Next(65, 90));
  21:             break;
  22:          }
  23:          case 3:
  24:          {
  25:             stRefID += char.ConvertFromUtf32(random.Next(97, 122));
  26:             break;
  27:          }
  28:       }
  29:  
  30:       System.Threading.Thread.Sleep(1);
  31:    }
  32:  
  33:    return stRefID; 
  34: }

The 2 keys to the update function are:

1) The Random() object is only instantiated once so the seed is only set once.

2) The addition of the Thread.Sleep(1) call between each of the time through the loop.  This makes sure that when the Next function is called the time has changed so that the number generated will not be the same as previously generated.

This is not a knock on the sample code I found, since it would work in certain instances.  But just a warning to be aware that when downloading code samples (even if they get good reviews), might not work in the situation that you need it for and IMO need to be tested even more than code you wrote yourself.

[AddThis]

12 Comments

  • I believe that your post is misleading for other developers. If you do not provide a valid seed value to 'Random' class, it reads 'System.Environment.TickCount' internally (Reflector), which generates a same random number (becuase seed value is not different in a loop).

    To avoid this issue, you need to provide a valid seed as follow:

    // ------------ Code Sample ------------
    System.Random random = new System.Random();
    int nextRandom = random.Next(1, 1000);
    System.Console.WriteLine(nextRandom);

    for (int index = 0; index < 100; index++)
    {
    random = new System.Random(nextRandom);
    nextRandom = random.Next(1, 1000);
    System.Console.WriteLine(nextRandom);
    }
    System.Console.ReadKey();

    NOTE: Do not use Thread.Sleep(1) to get different 'Tick' value for seeding, as it's bad performance application.

  • 1) I will definitely look into the System.Security.Cryptography.RNGCryptoServiceProvider.

    2) As for using the sleep, as written it only adds 8 milliseconds to each call to GenerateID. For what this function will be used for at this time this is acceptable. IMO, whether or not the performance hit is an issue or not depends on the usage and in this case we have decided using the sleep is not a big deal in this case.

  • Mital Kakaiya, actually you statement "... which generates a same random number (because seed value is not different in a loop)." is misleading.

    You are correct that that if you do not provide a seed value the random function does use the TickCount. But within a loop you will get duplicate random number for iteration within the same millisecond, but once the millisecond changes the random number returned by Random.Next also changes.

    In the code I posted (w/o sleep) and in your code the random number generated would not be the same for every time through the loop. The random number would be the same but only for the number of iterations through the loop that is in the same millisecond. (In most cases that is every 4-5 random numbers). For example, without the Sleep(1) I would get the Generated ID's that looked like the following:

    1111SSSDBBBB7777hhhh
    777DDDFkkkk5555

    By adding the sleep(1) it garantees that each Random.Next call is made in the next tick (millisecond), thus I then get Generated ID's that look like this.

    S4gyf690kHuDda
    724RdGTjn876UhfdemK
    D28fHY86jmkA54BGadE

    Very big difference and definitely generating random ID's as expected. As stated in previous post, for what it is used for the 8 millisecond performance hit per call is acceptable.

  • Next() called on the same instance of System.Random will return new random numbers each call, even within the same millisecond. You do not need to Sleep().

    Your earlier problems were related to seeding, but if you are using the same instance, you don't run into them.

  • Dude, I love you! I actually have read that misleading article even though I used just random.nextDouble(). Random kept giving the same number for every object and I didn't understand why but you pointed out that System.Threading.Thred.Sleep. I had to give a little bit higher number for it as a parameter to fill my needs but your article was precious.

  • This uses Date Ticks and uses the last 5 digit in the 17 digit value. (These are the digits that change extremely quickly and are the best way to seed the Random function, other than more advanced "noise" techniques)

    Heres my function for Randomizing numbers.

    public int RandomInt(int intMin, int intMax)
    {
    long timestamp = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).Ticks);
    int seed = Convert.ToInt32(Convert.ToString(timestamp).Substring(11));
    Random r = new Random(seed);
    return r.Next(intMin, intMax);
    }

  • Random number in c be careful of some of the samples you find.. He-he-he :)

  • check the following codes:
    internal static class ppp
    {
    public static Int32 pp = 10;
    }
    class Random
    {
    public float uniform(float a, float b)
    {
    float o, temp;
    System.Random r = new System.Random(ppp.pp);
    temp=(float)r.NextDouble();
    ppp.pp = r.Next(1, 100);
    o = (b - a) * temp + a;
    return o;

    }

  • How about this:

    public class RandomGlobal
    {
    private static Random r;
    public static Random Random
    {
    get
    {
    if (r == null)
    r = new Random();
    return r;
    }
    }
    }

    Call e.g. RandomGlobal.Random.NextDouble()

  • @Mital Kakaiya your code is not correct here.

    What you are doing is giving users code that, unless used appropriately, will generate the same number.

    c# has a big issue here, and the best way to go is use alternative class. If you have to use Random, the Sleep is about the only way to guarantee randomnes.

    Please be sure of what you say before posting code. Incorrect codee just gets circulated and wastes users time.

    Congrats to smehaffie on seeing the issues of c# Random.

  • I just happend to run across this and wonder why you use the {} in case statements? They are not needed and just use extra keystrokes.

    switch (val)
    {
    case 1:
    do something here
    break;
    case 2:
    do something elese here
    break;
    }

    does the same as your code with less work.

  • Jim B,

    It is just habit, because not having {} only works if there is only one line of code before the break statement. Otherwise you need the {}. The same is true with an if/then statement. It is more a habit than anything and only 1 more keystroke since the editor add the ending curly bracket.

    Sorry I did not answer this earlier. Did not get any emails about this comment being entered.

Comments have been disabled for this content.