11

I'm looking at generating a random number between 1 and 5 million. The process doesn't have to be quick (although it would be good if it was), but it must be as random as possible (I know nothing is random). I have a variety of data sources for the seed.

I'm not sure if the .NET Random class is going to be good enough for this.

This will be used to select a winning ticket.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
LiamB
  • 18,243
  • 19
  • 75
  • 116
  • 5
    Clearly and precisely define "as random as possible". To get a signal that is actually "as random as possible" you need a source of entropy -- the seed -- that has more bits of entropy than the random item generated from that seed. So, if you already have a variety of data sources for the seed, and they are high-entropy, then you're already done. Just extract 24 bits from your source of entropy, and that gives you a number between 0 and 16 million that is **as random as possible given your source of entropy**. – Eric Lippert Feb 14 '10 at 16:51
  • 2
    Also, it would help tremendously if you described what you're going to be using this random number for. – Eric Lippert Feb 14 '10 at 16:52
  • 3
    Re: your update: Now you are no longer in the realm solely of math and technology, but in the realm of legal regulations. Most of the people reading this are not lawyers familiar with the laws regarding what characteristics a device which chooses the winner of a lottery must possess. I would recommend consulting with a lawyer and a statistician before you spend more time pursuing a technical solution. Certainly no *simple* "pseudo random" solution is going to be acceptable; one can easily work out what the next winning number is from the previous ones with a PRNG. – Eric Lippert Feb 15 '10 at 00:48
  • I cant see how anyone could ever work it out as the allocation would be random and the select of the winner would also be random. – LiamB Feb 15 '10 at 08:27
  • 8
    The fact that *you* cannot see how it would be worked out does not logically imply that *no one* can see how it can be worked out. Pseudo-random number generators are called "pseudo-random" because they are *not random*. They are predictable; given information about their past output you can work out their likely future outputs. Furthermore, it is not enough that a RNG be unpredictable; it must also be *unbiased*. – Eric Lippert Feb 17 '10 at 05:54
  • @Eric "one can easily work out what the next winning number is from the previous ones with a PRNG." For a cryptographic PRNG this is *hard*. One important property an (unbroken) crypto PRNG possesses is that it's *hard* to distinguish it from real random numbers. With *hard* meaning you need to brute-force a large secret. Of course the legal problems remain. – CodesInChaos Jul 16 '11 at 09:13
  • 1
    @CodeInChaos: Right, that's why I said that no **simple** pseudo-random solution -- like calling Random.Next -- is going to work. A crypto-strength randomness solution is likely to be sufficiently random to prevent fraud, but still might not meet the legal requirements. – Eric Lippert Jul 16 '11 at 15:12

8 Answers8

17

The System.Random class probably is good enough:

Pseudo-random numbers are chosen with equal probability from a finite set of numbers. The chosen numbers are not completely random because a definite mathematical algorithm is used to select them, but they are sufficiently random for practical purposes. The current implementation of the Random class is based on Donald E. Knuth's subtractive random number generator algorithm. For more information, see D. E. Knuth. "The Art of Computer Programming, volume 2: Seminumerical Algorithms". Addison-Wesley, Reading, MA, second edition, 1981.

The only thing you have to watch out for is that you don't reuse the same seed too often:

If the same seed is used repeatedly, the same series of numbers is generated. One way to produce different sequences is to make the seed value time-dependent, thereby producing a different series with each new instance of Random.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
  • 1
    See here http://connect.microsoft.com/VisualStudio/feedback/details/634761/system-random-serious-bug It is coded incorrectly and so may not be good enough – evolvedmicrobe Mar 20 '14 at 00:37
  • @evolvedmicrobe: Having run a bunch of statistical tests on a version of `System.Random` with a patch fixing that particular bug, it's not clear to me that there aren't other things about the RNG that should be more worrying. Certainly, it has several different issues: https://github.com/dotnet/corefx/issues/23298 – fuglede Sep 09 '17 at 14:10
7

If you need cryptographic random number, go with the System.Security.Cryptography.RNGCryptoServiceProvider class or use the RandomNumberGenerator.Create() factory method to create the default configured random number generator.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • 3
    RNGCryptServiceProvider works on filling byte arrays so you'd need to convert back to an int and check it's in the right range – zebrabox Feb 14 '10 at 16:57
  • 1
    +1 for @zebrabox's comment, plus: if the generated value is out of range, do NOT mod it (% operator) since that may cause a bias for some values. Loop around and generate another value instead. – devstuff Jun 20 '10 at 21:55
  • @devstuff Which is one more reason not to use `System.Random` since their implementation of `Next(...)` are biased(They don't seem to use `%` but the numbers are not equally likely.) – CodesInChaos Jul 16 '11 at 09:16
  • @CodeInChaos: But `System.Random` can *not* be used for cryptographic numbers, which is what my answer is about. – Steven Jul 16 '11 at 11:40
  • Of course. But it's even flawed for non cryptographic numbers since it has a flaw similar to the one @devstuff mentioned. – CodesInChaos Jul 16 '11 at 11:45
5

See Jon Skeet's blog post Revisiting Randomness a very good review of how to use Randomness:

Revisiting randomness
Almost every Stack Overflow question which includes the words "random" and "repeated" has the same basic answer. It's one of the most common "gotchas" in .NET, Java, and no doubt other platforms: creating a new random number generator without specifying a seed will depend on the current instant of time. The current time as measured by the computer doesn't change very often compared with how often you can create and use a random number generator – so code which repeatedly creates a new instance of Random and uses it once will end up showing a lot of repetition.

more...

Metro Smurf
  • 37,266
  • 20
  • 108
  • 140
5

There was actually a really good article I read fairly recently on different types of PRNGs and how they fare in terms of several different randomness tests. Unfortunately, I can't seem to find it now. The gist of it, however, was that the default random number generators in almost every popular programming language are quite naïve and have pretty significant biases.

Another answer already mentions that no PRNG at all, no matter how sophisticated the algorithm, is good enough for cryptographic applications. This is true. Since you mention that this will be used to "select a winning ticket", let's ignore that for now.

The Knuth algorithm used by the .NET System.Random class is optimized mainly for speed, not random distribution. It is "random enough" for many purposes, which most applications never stray too far from, but in the fields of (a) gaming and (b) statistical simulation, most people seem to think that it is a poor choice. It's better than the LCGs that used to be the default in older libraries, but you still don't want to be using it for something like a lotto.

Don't get fooled into thinking that you just use a crypto source, either. The problem with crypto RNGs is that they fill a stream of bytes, but turning this into a single random integer between x and y requires that you do some modular arithmetic (or rounding - same result either way). And if your random range doesn't divide perfectly evenly into whatever power of 2 is defined by the byte length, then you're going to end up with a bias in the lower numbers. The generated data has high entropy, but your result will be biased.

As a simple example, let's just say you get a "perfect" random number from 1 to 10 and now you want to turn that into a random number between 1 and 7. How do you do it? Simply calculating result % 7 will be heavily biased toward the numbers 1-3. There are some ways to reduce the bias when using a crypto RNG but the point I'm trying to make is that crypto RNGs are designed for crypto applications, and using one of those for a Monte Carlo simulation isn't usually the best idea.

As far as I know, the most popular "good" PRNG today, which is used commonly in gaming applications, is the Mersenne Twister. There's a .NET implementation here. This algorithm passes all of the Diehard Tests for random distribution; it shows almost no bias and is a good choice when you are using random numbers for probabilistic and statistical applications.

The GNU Scientific Library also has a number of RNG algorithms and, not surprisingly, the Mersenne Twister is at the top of the list. Some of the others are worth looking at for curiosity's sake, though; RANLUX also scores pretty high on the diehard test IIRC.

Eric is correct with his comment, of course; all of this information is for naught if you don't have specific technical requirements on "how random" you need your random numbers to be. I'm using a definition that would be applicable to a relatively low-impact gambling/gaming application (i.e. not a major registered gambling site with millions of visitors per day - there are stricter rules on randomness for those).

Aaronaught
  • 120,909
  • 25
  • 266
  • 342
  • The C# library is not based on Knuth's method, they goofed coding it – evolvedmicrobe Mar 20 '14 at 00:39
  • @evolvedmicrobe: [That's what their documentation says](http://msdn.microsoft.com/en-us/library/system.random(v=vs.110).aspx). Do you have a citation or some evidence to prove otherwise? – Aaronaught Mar 21 '14 at 21:50
  • yes, they attempted it here and I have verified myself by decompiling: http://connect.microsoft.com/VisualStudio/feedback/details/634761/system-random-serious-bug – evolvedmicrobe Mar 21 '14 at 22:01
  • @evolvedmicrobe: Fair enough, although I hardly think such a minor discrepancy makes any difference as to the substance of this answer (or Chris's, for that matter - either a PRNG meets the requirement or it doesn't, makes no difference what the period is). – Aaronaught Mar 22 '14 at 19:15
  • @aeronaught not saying it affects the substance of the answer, and the original question was so lacking in specific requirements who knows what the answer to that is. But people reading the post (or the MSDN documentation) might have thought the speedy one was based on Knuth, which it wasn't, so thought I would save others from being tripped up the way I was. – evolvedmicrobe Mar 23 '14 at 00:30
4

To generate a random number, create an object of Random class, and then use Next function of this object to generate a random number. It has many overloads like:

Next(int minimum, int maximum);
Next(int Maximum);

where you can specify the minimum and maximum range between which you want the random number.

Code snippet:

Random random = new Random();
int maxValue = 100;

int r = random.Next(maxValue);
nhahtdh
  • 55,989
  • 15
  • 126
  • 162
Sameer
  • 43
  • 3
3

The System.Random class is very problematic and isn't a good fit with the requirement. In theory, it should provide better results than many other pseudo-random generators out there. It is a direct and literal port of example C code for a Lagged Fibonacci Generator (LFG) provided on page 283 of the second edition of 'Numerical Recipes in C' (the code was re-written in later editions). LFGs use a better algorithm than Linear Congruential Generators (LCGs) used is many other libraries (e.g., Java).

Unfortunately, Microsoft's implementation of System.Random class has a bug in it. See http://connect.microsoft.com/VisualStudio/feedback/details/634761/system-random-serious-bug for more information. It seems someone accidently typed in '21' when the meant to type in '31'. This compromises the algorithm's pseudo-random characteristics. The link includes an explanation from MS as to why they feel unable to fix the error at this stage.

Charles
  • 31
  • 1
  • Thanks for the detailed response. We ended up using a hardware rng but thanks for the info and the link! – LiamB Nov 03 '13 at 13:29
  • There's another bug in the mapping of negative seed values. These are mapped to their absolute value, but if the seed is `int.MinValue` it is mapped to `int.MaxValue`. This means that there are three input seeds that map to int.MaxValue and only one that maps to zero, while every positive integer other than int.MaxValue corresponds to two input values. This implies that there is one sequence that has a 50% higher probability of being chosen and one that has a 50% lower probability. Unfortunately the connect link is dead. – phoog Jun 26 '20 at 14:40
1

If you're looking for true random numbers then you should consider using an online random number generator that uses natural phenomenon, such as http://www.random.org, which uses atmospheric noise. True random numbers also make good seeds for psuedo-random number generators.

Sipwiz shows how to use it in C# in his answer: Generate random values in C#. It's also discussed here: http://www.vcskicks.com/random-number-generator.php.

There's a lot of angles to random number generators. An interesting alternative implementation is ISSAC (ttp://burtleburtle.net/bob/rand/isaac.html), which also contains a good discussion of bias and such, and there's a C# version, too (http://burtleburtle.net/bob/rand/isaacafa.html).

Community
  • 1
  • 1
Ed Power
  • 8,310
  • 3
  • 36
  • 42
-1

.NET's Random should be fine for this:

var random = new Random(System.DateTime.Now.Millisecond);

int randomNumber = random.Next(1, 5000000);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Michael S. Scherotter
  • 10,715
  • 3
  • 34
  • 57
  • 1
    This only works for a local variable, that's an accident waiting to happen. – Hans Passant Feb 14 '10 at 17:41
  • Should be int randomNumber = random.Next(1, 5000001); as the .Net method returns a number inclusive of the lower limit but exclusive of the upper limit. – Ed Power Feb 14 '10 at 18:11
  • It depends on the context where the number is used in if a millisecond is a good enough seed. Slot machines have been hacked this way. – Paco Feb 14 '10 at 19:48
  • 4
    this only offers 1000 different seeds, changes only rarely,... This is even worse than just using `new Random()` – CodesInChaos Jul 16 '11 at 09:09