4

As RNGCryptoServiceProvider is "safer" (produces high-quality random numbers) than Random() I felt for using it. Performance is not an issue. But, instead of reading the last digit, and somehow decide when to add a 0 or 1 before it.. Is there a better (more accurate) way?

byte[] data = new byte[4];
rng.GetBytes(data);
int value = BitConverter.ToInt32(data, 0);
Console.WriteLine(value);
Half_Baked
  • 340
  • 4
  • 18
  • 1
    "Safer"? What does that mean? An [APC](http://en.wikipedia.org/wiki/Armoured_personnel_carrier) is safer than my car, but I don't think it's worth it going to work with one. – Jon May 09 '13 at 12:05
  • 1
    Produces better random numbers – Half_Baked May 09 '13 at 12:07
  • "Better" in what sense? – Dan Puzey May 09 '13 at 12:08
  • "Better" is not a scientific term either. It produces *cryptographically secure* random numbers. If you are not using them for crypto that is immaterial. Also, by modifying the numbers yourself you are in effect destroying their crypto-strength property. So what do you gain? – Jon May 09 '13 at 12:08
  • http://www.azillionmonkeys.com/qed/random.html – Patashu May 09 '13 at 12:10
  • 4
    @Jon `System.Random` isn't ideal even for non cryptographic numbers. It's difficult to seed, it has significant biases in some situations,... – CodesInChaos May 09 '13 at 12:11
  • @CodesInChaos: "Ideal" depends on what you use it for. I 'd be interested in reading about its shortcomings if you have any links. – Jon May 09 '13 at 12:15
  • @Jon An [older rant about `System.Random`](http://stackoverflow.com/a/6842191/445517) – CodesInChaos May 09 '13 at 12:32
  • @CodesInChaos: Interesting, although frankly only #2 seems a genuine drawback to me (seeding is "you get what it says on the box" and perf is offset by the very convenient interface). How did you come up with `0x55555555`? Also, a fair comparison should mention that exhausting the entropy pool (as mentioned in another answer) could be a problem. – Jon May 09 '13 at 12:41

3 Answers3

8

You can use the modulo operator (%). This leads to slightly biased results, but with an 8 byte input the bias is quite small. Much smaller than the bias System.Random has.

byte[] data = new byte[8];
rng.GetBytes(data);
ulong value = BitConverter.ToUInt64(data, 0);
result = (int)(value%15+1);

Or if you want perfectly uniform numbers:

byte[] data = new byte[8];
ulong value;
do
{
    rng.GetBytes(data);
    value = BitConverter.ToUInt64(data, 0);
} while(value==0);
result = (int)(value%15+1);
Spooky
  • 2,966
  • 8
  • 27
  • 41
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • To be clear, though, it is possible to get a perfectly unbiased uniform distribution - by observing that 2^64 % 15 is 1, and thus if you discard and reroll if you get exactly `0` you end up with an exact multiple of 15 values you can roll left - perfectly divisible. – Patashu May 09 '13 at 12:12
  • Ah, that was nice! I'm guessing it would still produce better quality random numbers than using Random() – Half_Baked May 09 '13 at 12:12
  • @Patashu So throw / ignore result if it's exactly 0? – Half_Baked May 09 '13 at 12:14
  • @Half_Baked Simply reject the number and try again. – CodesInChaos May 09 '13 at 12:17
  • @CodesInChaos Thanks man! Really appreciate you taking the time to write the code example! – Half_Baked May 09 '13 at 12:19
  • @CodesInChaos Hm, I get too high values if I do this result = (int)(value%200+50); Should I be getting values between 200 and 50? – Half_Baked May 17 '13 at 19:00
  • @Half_Baked %200 means you get 200 different values => `value%200+50` returns values from 50 to 249. – CodesInChaos May 17 '13 at 19:47
  • @CodesInChaos Oh ok, so it should be value%(max-min)+min ? – Half_Baked May 17 '13 at 19:53
  • @Half_Baked Yes. But you need to be careful about exclusive maximum vs. inclusive maximum. If you want an inclusive maximum you need to use `value % (max - min + 1) + min`. Another potential issues are integer overflows if the difference between min and max exceeds two billion. – CodesInChaos May 17 '13 at 19:59
  • @CodesInChaos So incelusive maximum (max - min + 1) is safer? I will only generate random numbers from 0 to 300.000 approx – Half_Baked May 17 '13 at 20:06
0

It's easy:

static int getnum15(RNGCryptoServiceProvider RngCsp)
{
    byte[] p=new byte[1];
    do {
        RngCsp.GetBytes(p);
    } while (p[0]==255);
    return(p[0]%15);
}

because 256 mod 15=1. Then

RNGCryptoServiceProvider rngCsp=new RNGCryptoServiceProvider();
for (int i=0;i<30;i++)
    Console.WriteLine(getnum15(rngCsp));
-2

There is no way to make the RNGCryptoServiceProvider give you numbers, because it is not intended for arithmetics. In cryptography, you work with words and bytes. If want a different RNG for arithmetic purposes, pick a different library.

Jonny
  • 1,453
  • 16
  • 25
  • 1
    Even in cryptography you sometimes need numbers from 0 to n-1. For example when creating random alphanumeric tokens. Personally I always use a crypto grade PRNG, even for numerical work. – CodesInChaos May 09 '13 at 12:26