-2

I have two separate functions that each generate a random number from the same range (1-5). Unfortunately, ~90% of the time they generate the same number. I know that within a single function you solve that problem by re-using the same Random object because it's happening so quickly that the system clock has yet to really change and seed a different "random" value, but when it's spread across more than one function what is the best approach to resolving this? The whole process takes about a full second to complete and these functions are not called back-to-back, so surely (I would think) the clock has ticked in between them, but is that not true?

I have some ideas for how to generate the numbers using an algorithm instead of just Random but they would be more taxing and I feel there's a simpler solution.

Edit: Can the people who downvoted please explain? I'd be happy to improve the question based on your suggestions.

Community
  • 1
  • 1
thanby
  • 323
  • 1
  • 6
  • 22
  • 1
    Create a random number provider class and pass it to consumers via dependency injection. – Ilian Mar 09 '16 at 00:30
  • one idea would be to pass the same random number generator to both method calls instead of each method call having its own – Will Newton Mar 09 '16 at 00:30
  • 1
    Please show the code that uses this. Often when you see this behavior the list you are populating/manipulating is actually being shared between two objects when you are not expecting them to. – Scott Chamberlain Mar 09 '16 at 00:34
  • seed you random number generator using Guid.NewGuid().GetHashCode – johnny 5 Mar 09 '16 at 00:47
  • 1
    Do **not** seed your random number generator with a hash code. It is not designed to be a source of entropy. Better sources of entropy are available. Hash codes are only approximately random; there is no guarantee, for example, that their low order bits have an acceptable level of entropy. – Eric Lippert Mar 09 '16 at 01:05
  • Out of curiosity, why the downvotes? – thanby Mar 09 '16 at 17:28

3 Answers3

7

Ways out of this hole:

  • Pass a single Random object around as needed.

  • Make the Random object a static public field of a class visible to everything that needs it. Note that Random is not thread safe. If you need it to be thread safe then either put locks around it, or make it thread local and allocate one for each thread.

  • Use crypto strength randomness. It doesn't have this problem.

  • Write your own pseudo random number generator that has better behaviour than the built-in one.

  • Use another source of randomness entirely, like downloading a blob from random.org or some such thing.

A now-deleted answer suggests using NewGuid as a source of randomness. Do not do this. Guids are guaranteed to be unique; they are not guaranteed to be random. In particular, it is perfectly legal for a guid generator to generate sequential unique guids. That NewGuid does not actually do so is not part of its contract. Use guids for what it says on the box: the box says "globally unique identifier", not "randomly generated identifier".

A commenter suggests using a hash code as a source of randomness. Do not do this. Hash codes are guaranteed to be randomly distributed only so far as they need to be to give a good distribution in a hash table. In particular, we have no guarantee whatsoever that hash functions were designed to ensure that their low order bits are well distributed.

Use only sources of randomness or pseudo-randomness that were designed by experts to produce randomness. Generating randomness is a difficult engineering problem. Just because you think you can't predict a particular outcome does not make that outcome a suitable source of entropy for a random number generator.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • what wrong with using the GetHashCode from the NewGuid as A seed, the default implementation of GetHashCode should vary enough even if the guids are similar or sequential – johnny 5 Mar 09 '16 at 00:51
  • 3
    @johnny5: **Do not rely on undocumented implementation details to obtain something as important as randomness**. People have this terrible idea that randomness is easy to come by, and that if they don't understand a process, it must be random. Here, let's just pick an example. Can you provide for me a **proof** that taking the low-order-bit of a sequence of get hash codes of a sequence of guids never produces a long string of identical bits? – Eric Lippert Mar 09 '16 at 01:00
  • 3
    @johnny5: That the low order bit of a random sequence is itself random is an important property of random sequences. **Do you believe that the people who wrote the hash code implementation for guids and the generator for guids designed it to have that property**? If not, then it has that property either (1) not at all, or (2) entirely by accident. That is the very definition of unreliable. – Eric Lippert Mar 09 '16 at 01:01
  • 3
    @johnny5: Moreover, it staggers my mind the number of people who try to get randomness out of things that were not designed to produce randomness **when there are strong sources of randomness designed by experts available**. Use the tools you've been given that were designed by people who knew what they were doing, instead of cobbling together stuff out of parts that were designed to do something completely different. **Act like an engineer, not a scavenger**. – Eric Lippert Mar 09 '16 at 01:02
  • One would think a topic like generating a random number would already be answered, locked, public wiki, or whatever SO uses for such things, and this question would be a duplicate without the need for such lengthy explanations, contradicting opinions, etc. – Kory Gill Mar 09 '16 at 01:33
  • 2
    @KoryGill: This question is asked almost every day. It is a testament to the poor design of the Random class. It should never have had the property that creating two in short succession would cause non-random sequences. – Eric Lippert Mar 09 '16 at 01:34
  • I'd just like to point out that for option 4 the way to do that is really to just create a wrapper around `Random` that just has a more sensible default seed; trying to create the entire thing from scratch is problematic for reasons listed out later in the answer, and simply isn't necessary as the only real problem here is the default seed/default instance. – Servy Mar 09 '16 at 03:32
  • I think I'll go the route of building my own random number generator that I can re-use across projects, it will be a good programming/math exercise if nothing else. Thanks for the advice. – thanby Mar 09 '16 at 16:16
  • @EricLippert GetHashCode, is designed to minimize collisions. The only thing that could cause an issue is that the guids generated constantly cause a collisions, there is no reason to over-engineer a solution, for something that has such a low probability of occurrence. Seeding using the hashcode of guid is by all means practical compared to downloading a blob from random.org – johnny 5 Mar 09 '16 at 16:33
2

A quick solution is to share the random seed between your consumers.

One way to do that is creating a provider:

public class RandomIntegerProvider 
{
     private static Random _globalRandomGenerator = new Random();

     public int Next()
     {
       //...
     }
}

Edit:

A very interesting post about that:

http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx

You can play with different strategies and even ensure thread safety.

gustavodidomenico
  • 4,640
  • 1
  • 34
  • 50
0

The OOP-friendly way would be to pass it through a parameter to a function, or through a constructor parameter to a class, where it would be stored as a field / property.

This is called dependency injection - it's what you should be doing, if you want your applications to be testable and scalable!

An example:

void Caller()
{
    Random random = new Random();
    Foo(random);
    Bar(random);
}

void Foo(Random random)
{
    int randomNumber = random.Next();
}

void Bar(Random random)
{
    int randomNumber = random.Next();
}
Gediminas Masaitis
  • 3,172
  • 14
  • 35
  • Sorry but I disagree with some statements. One of them is about the "dependency injection" and it's relation to testability. The correct principle is Dependency Inversion and not necessary achieved by the injection. – gustavodidomenico Mar 09 '16 at 00:47
  • @gustavodidomenico This is perhaps not the right place for discussion, but are you saying that *dependency injection doesn't help achieve testability*? – Gediminas Masaitis Mar 09 '16 at 00:56
  • Not directly. However, using dependency **injection**, we usually have the benefit of dependency **inversion**. They are not the same. – gustavodidomenico Mar 09 '16 at 02:04