19

Possible Duplicate:
Why does it appear that my random number generator isn't random in C#?
How can I generate truly (not pseudo) random numbers with C#?

I've created a dice game where the dice is based on a percentile, 1-100.

public static void Roll()
{
    Random rand = new Random((int)DateTime.Now.Ticks);
    return rand.Next(1, 100);
}

But I don't feel like it's a real random based on current time.

If I do

for (int i = 0; i < 5; i++)
{
   Console.WriteLine("#" + i + " " + Roll());
}

They would all be the same values, because the DateTime.Now.Ticks didn't change, it seeded the same number.

I was thinking I could generate a new random seed if the seed was the same due to the current time, but it doesn't feel like an honest "re-roll"

What should I do to try and replicate a close to real/honest dice roll? Should I use the RNGCryptoServiceProvider class to generate rolls instead?

Community
  • 1
  • 1
Kyle
  • 3,004
  • 15
  • 52
  • 79
  • I suggest reading this: http://csharpindepth.com/Articles/Chapter12/Random.aspx – Oded Nov 29 '11 at 10:05
  • 2
    there are many variations of this question on SO. Here's one of them... [Why does it appear that my random number generator isn't random in C#?](http://stackoverflow.com/questions/932520/why-does-it-appear-that-my-random-number-generator-isnt-random-in-c) ... Marking as dupe. (You should only have a single static instance of your Random rather than a new Random for each Roll) – spender Nov 29 '11 at 10:07
  • It's generally best to copy-and-paste when quoting code. I assume you must have retyped, because you can't return a value out of a `void` function, but `Roll` is declared as `public static void Roll()`. – T.J. Crowder Nov 29 '11 at 10:10
  • @rapsalands: A true random would be a truly random sequence. `Random` won't do that for the OP, I've pointed that out in my answer. :-) – T.J. Crowder Nov 29 '11 at 10:11
  • @spender: Except that the OP says he wants a **true** random number. Now, he probably doesn't, he's probably just asking that `Random` question, but... :-) – T.J. Crowder Nov 29 '11 at 10:18
  • @TJ hey! :p I really wanted an honest roll instead of a "fixed" roll you know? I'm going to use `RNGCryptoServiceProvider` for this need since that class was made for cryptography--and it should work for this game. – Kyle Nov 29 '11 at 10:22
  • @spender Actually read that question and in particular the first answer - this question is not a duplicate of that question. – Justin Nov 29 '11 at 10:45
  • @Justin... oops. You're right. I'll dig out another! – spender Nov 29 '11 at 10:46

6 Answers6

19

DateTime.Now.Ticks only has a resolution of approximately 16ms, so if you create a Random with that overload multiple times within a 16ms "slot" they will all be seeded with the same value and therefore you will get the same sequence.

Initialize your Random outside your loop so that a single Random sequence is produced, rather than creating it each time within the loop which could result in Randoms being seeded with the same value and so produce the same sequence.

Update

My previous point that the default constructor initialized Random with CPU ticks was incorrect, the default constructor actually uses Environment.TickCount which is:

A 32-bit signed integer containing the amount of time in milliseconds that has passed since the last time the computer was started.

Which still has a low resolution. If you make multiple instances of Random in quick succession, they can easily be created within the same time slot and therefore have the same seed value, and create the same sequence. Create a single instance of Random and use that.

Update

Further to your comments, if you wish to generate a random sequence across multiple threads, please see the following Jon Skeet article which discusses a thread-safe wrapper:

https://codeblog.jonskeet.uk/2009/11/04/revisiting-randomness

Sam
  • 1,384
  • 2
  • 20
  • 30
Tim Lloyd
  • 37,954
  • 10
  • 100
  • 130
  • The default random() seed is current CPU ticks? – Kyle Nov 29 '11 at 10:14
  • @Kyle I have updated my answer. – Tim Lloyd Nov 29 '11 at 10:26
  • Thanks. ^.^ I don't know if I should still use random because I use threads. I've read random isn't thread safe. If I initialize random() outside of the function I think it will cause problems. – Kyle Nov 29 '11 at 10:31
  • 1
    @Kyle Please see the following article from Jon Skeet which discusses a thread-safe approach to using `Random`: http://msmvps.com/blogs/jon_skeet/archive/2009/11/04/revisiting-randomness.aspx. – Tim Lloyd Nov 29 '11 at 10:34
9

Pseudo-random number generators like Random should only be seeded once, so:

private static Random _rand = new Random();
public static int Roll()
{
    return _rand.Next(1, 100);
}

(Note I made the return value int rather than void; the Roll function as quoted in the question results in a syntax error.)

But your title says "Creating a true random". Random won't do that for you, it's a pseudo-random number generator, meaning it's deterministic, just hard to predict if you don't know the seed. Usually that's good enough for most purposes, but if you need real randomness, you need an entropy source. http://random.org is one popular one.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Thank you for mentioning that. I was rewriting it in the thread based on memory it was a syntax error. Thank you for the help. – Kyle Nov 29 '11 at 10:18
4

You should create your Random class only once outside your Roll function and seed it with a unique value.

You are recreating your Random each time you call Roll which causes the 'not random numbers'.

Wouter de Kort
  • 39,090
  • 12
  • 84
  • 103
1

Should I use the RNGCryptoServiceProvider class to generate rolls instead?

If this is a serious game with money at stake then: Yes.

H H
  • 263,252
  • 30
  • 330
  • 514
  • 1
    Money isn't at stake. I wanted to replicate realistic rolls. So when people play it feels like they're really rolling a dice. – Kyle Nov 29 '11 at 10:17
  • Random will be more than enough, but read the other answer about seeding. – H H Nov 29 '11 at 10:18
  • I'll be using this because random() isn't thread safe. I learned some more things about random() today though. – Kyle Nov 29 '11 at 10:30
  • RNGCryptoServiceProvider is now deprecated. Use System.Security.Cryptography.RandomNumberGenerator – Chris Jensen Jul 06 '22 at 13:56
0

I'm assuming that you are calling the Roll() method so quickly that Now.Ticks is the same?

The simplest way to get around this would be rather than to create a new Random() instance each time you call Roll() create a static variable to hold a single instance of Random().

samjudson
  • 56,243
  • 7
  • 59
  • 69
0

The usual way to use random number generators is to seed them once, save them and call on them repeatedly throughout your programme. As long as you seed from a suitable value at the start, you should get acceptable randomness - assuming the generator you're using is using a function returning things which are suitably random for your purposes. So, save your Random instance outside the Roll() function, seed it the first time it's used, then just call Next() on it each time you need another number.

When you get right down to it, there's no such thing as true random number generation on a computer, only pseudorandom sequences based on a seed. However, humans are terrible at identifying randomness, so it's usually okay.

Matthew Walton
  • 9,809
  • 3
  • 27
  • 36