views:

1107

answers:

12

I'm working in Microsoft Visual C# 2008 Express.

I found this snippet of code:

    public static int RandomNumber(int min, int max)
    {
        Random random = new Random();

        return random.Next(min, max);
    }

the problem is that I've run it more than 100 times, and it's ALWAYS giving me the same answer when my min = 0 and max = 1. I get 0 every single time. (I created a test function to run it - really - I'm getting 0 each time). I'm having a hard time believing that's a coincidence... is there something else I can do to examine or test this? (I did re-run the test with min = 0 and max = 10 and the first 50ish times, the result was always "5", the 2nd 50ish times, the result was always "9".

?? I need something a little more consistently random...

-Adeena

A: 

in VB i always start with the Randomize() function. Just call Randomize() then run your random function. I also do the following:

Function RandomInt(ByVal lower As Integer, ByVal upper As Integer) As Integer
 Return CInt(Int((upper - lower + 1) * Rnd() + lower))
End Function

Hope this helps! :)

Jason
why is this marked down? i consistently get random numbers from this...
Jason
Because we're discussing C#
TheSoftwareJedi
are you for real? do you know how many times i've gotten C# answers to VB questions and was able to figure out what i was doing wrong? it's the concept, man, not the syntax... jeezz...
Jason
Every single one of those functions is only available in VB (unless you reference Microsoft.VisualBasic.dll)
SLaks
The System.Random class is completely different.
SLaks
+6  A: 

The min is inclusive, but the max is exclusive. Check out the API

AgileJon
+24  A: 
random = new Random();

This initiates random number generator with current time (in sec). When you call your function many times before system clock changed, the random number generator is initiated with the same value so it returns same sequence of values.

mateusza
The documentation for the Random() constructor describes this issue almost verbatim: http://msdn.microsoft.com/en-us/library/h343ddh9.aspx
las3rjock
+33  A: 

The problem with min = 0 and max = 1 is that min is inclusive and max is exclusive. So the only possible value for that combination is 0.

Annath
+6  A: 

That overload of Next() returns:

A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not MaxValue. If minValue equals maxValue, minValue is returned.

0 is the only possible value for it to return. Perhaps you want random.NextDouble(), which will return a double between 0 and 1.

Dave Bauman
+14  A: 

Don't create a wrapper method for Next. It wastes cycles creating a new instance of the Random class. Just use the same one!

Random myRand = new Random();

for(int i = 0; i < 10; i++)
{
    Console.WriteLine(myRand.Next(0, 10).ToString());
}

That should give you ten random values.

As has been said--Random is pseudo-random (as all implementations are), and if you create 100 instances with the same seed, you'll get 100 instances of the same results. Make sure that you're reusing the class.

Also, as folks have said, beware that MinValue is inclusive and MaxValue is exclusive. For what you want, do myRand.Next(0, 2).

Eric
Doing it 10 or more times was just the test... I didn't realize that "max" was exclusive.
adeena
Sometimes you will want a wrapper method (or more likely a wrapper class) for Random so that you can mock it.
BlackWasp
@adeena, BlackWasp: If you need to overload/wrap Next for whatever reason, go for it. If you're looking to just get another random value, though, use Next and use the same Random class. That way, you're not at too much risk of getting a bunch of Random's with the same seed.
Eric
+3  A: 

Besides the 0-1 issue already noted in other answers, your problem is a real one when you're looking for a 0-10 range and get identical results 50 times in a row.

new Random() is supposed to return a random number with a seed initialized from the timer (current second), but apparently you're calling this code 50 times a second. MSDN suggests: "To improve performance, create one Random to generate many random numbers over time, instead of repeatedly creating a new Random to generate one random number.". If you create your random generator once outside the method, that should fix your "non-randomness" problem as well as improving performance.

Also consider this post for a better pseudo-random number generator than the system-supplied one, if you need "higher quality" pseudo-random numbers.

Alex Martelli
+1  A: 

As others have mentioned, the Random being built multiple times per second uses the same second as the seed, so I'd put the Random constructor outside your loop, and pass it as a parameter, like this:

public static int RandomNumber(Random random, int min, int max)
{
    return random.Next(min, max);
}

Also as mentioned by others, the max is exclusive, so if you want a 0 or 1, you should use [0,2] as your [min,max], or some larger max and then do a binary AND with 1.

public static int RandomOneOrZero(Random random)
{
    return random.Next(0, int.MaxValue) & 1;
}
Chris Doggett
The Random object could also be declared as a static variable, so it doesn't need to be passed to the method every time.
Whatsit
I like the RandomOneOrZero method. Are you using it because you know it to be more random than using [0,2] or for another reason?
Matt Boehm
@Matt: It'd be the same amount of randomness either time. I just whipped it up off the top of my head.@Whatsit: Good point. I wasn't sure what her use case was, so I just guessed at it. A static or instance variable would probably be more appropriate.
Chris Doggett
+4  A: 

You're always getting 0 because Random.Next returns integers. You need to call Random.NextDouble, which will return a number between 0 and 1. Also, you should reuse your Random instance, like this:

[ThreadStatic]
static Random random;
public static Random Random { 
    get {
        if (random == null) random = new Random();
        return random;
    }
}
public static int RandomInteger(int min, int max)
{
    return Random.Next(min, max);
}
public static double RandomDouble() //Between 0 and 1
{ 
    return Random.NextDouble();
}

If you want cryptographically secure random numbers, use the RNGCryptoServiceProvider class; see this article

EDIT: Thread safety

SLaks
-1 because this code is dangerously wrong. Random is not thread-safe and you're accessing a shared instance without providing any locking or a per-thread instance.
Greg Beech
Fixed; thanks for the advice
SLaks
Cool, -1 removed.
Greg Beech
+1  A: 

This is an addendum to any answers, as the answer to this specific question is the bounds should be (0, 2) not (0, 1).

However, if you want to use a static wrapper method, then you must remember that Random is not thread-safe, so you either need to provide your own synchronization mechanism or provide a per-thread instance. Here is a largely non-blocking implementation which uses one generator to seed each per-thread generator:

public static class ThreadSafeRandom
{
    private static readonly Random seed = new Random();

    [ThreadStatic]
    private static Random random;

    public static int Next(int min, int max)
    {
        if (random == null)
        {
            lock (seed)
            {
                random = new Random(seed.Next());
            }
        }

        return random.Next(min, max);
    }

    // etc. for other members
}
Greg Beech
Environment.TickCount (the default seed) is good enough for a seed; there's no point in making a separate seed instance.
SLaks
@SLaks - If you rely on Environment.TickCount then you'll quite possibly get the same seed for each per-thread instance, which will cause them to produce the same sequence of pseudo-random values. I very much doubt that this is what you want. By giving them an explicitly different seed, you prevent this behaviour pattern from occuring.
Greg Beech
A: 

Several posters have stated that Random() uses a seed based on the current second on the system clock and any other instance of Random created in the same second will have the same seed. This is incorrect. The seed for the parameterless constructor of Random is based on the tick count, or number of milliseconds since boot time. This value is updated on most systems approximately every 15 milliseconds but it can vary depending on hardware and system settings.

Stephen Martin
A: 

I found a very simple, but effective way to generate random numbers by just taking the last two digits of the current datetime milliseconds:

   int seed = Convert.ToInt32(DateTime.Now.Millisecond.ToString().Substring(1, 2));
   int cnr = new Random(seed).Next(100);

It is crude, but it works! :-)

Of course it would statistically generate the same number every hundred times. Alternatively, you could take all three digits or concatenate with other datetime values like seconds or so.

Fedor Steeman