Recently I was looking into shuffling a deck of cards and how to implement that in C#. It turns out that shuffling itself is not that hard, but verifying that a deck is shuffled correctly is a lot harder! I won't go into that here. Anyway, shuffling needs a random number generator. I'm familiar with System.Random, but it turns out that there's also System.Security.Cryptography.RandomNumberGenerator. I'm interested in the difference between the two and if one is better to use or not.
The setup
I want to generate numbers between 1 and 52 (inclusive) to simulate picking a card from a deck. To check how random the numbers are distributed I generate 5.2 million numbers. I expect every number to be picked on average 100,000 times.
For System.Random I instantiate Random once and use that instance for every number generated. I use the following method:
For System.Random I instantiate Random once and use that instance for every number generated. I use the following method:
Random random = new(); int number = random.Next(1, 53); // maxValue is exclusive.
For System.Security.Cryptography.RandomNumberGenerator I use the recommended static method, so no need to instantiate anything:
int number = RandomNumberGenerator.GetInt32(1, 53); // fromInclusive, toExclusive
I also record the time it takes to instantiate random (if required for the method) and generate all the numbers. The time taken includes registering the number in a dictionary. Finally I register the difference of the numbers taken from the expected average to see if Random (Rnd) or RandomNumberGenerator (Rng) differ much.
The results
Num Rnd RndDev Rng RngDev #1: 99856 144 99973 27 #2: 100359 359 100596 596 #3: 99311 689 100068 68 #4: 100271 271 100169 169 #5: 100288 288 99930 70 #6: 99576 424 100045 45 #7: 99198 802 99927 73 #8: 100038 38 100325 325 #9: 99636 364 100263 263 #10: 99759 241 100325 325 #11: 99633 367 99899 101 #12: 100012 12 100072 72 #13: 99668 332 100320 320 #14: 100290 290 99867 133 #15: 100445 445 100572 572 #16: 100135 135 100288 288 #17: 100380 380 100122 122 #18: 100098 98 99842 158 #19: 100638 638 100046 46 #20: 99551 449 99809 191 #21: 100281 281 99971 29 #22: 100174 174 100228 228 #23: 100070 70 99795 205 #24: 100289 289 99702 298 #25: 99442 558 100057 57 #26: 99652 348 99482 518 #27: 100589 589 99726 274 #28: 100446 446 100278 278 #29: 99867 133 99850 150 #30: 100226 226 99504 496 #31: 99994 6 99833 167 #32: 100083 83 99699 301 #33: 100142 142 100026 26 #34: 99869 131 99953 47 #35: 99521 479 99683 317 #36: 100066 66 100725 725 #37: 99982 18 100256 256 #38: 99631 369 100474 474 #39: 100239 239 100261 261 #40: 99822 178 99693 307 #41: 99973 27 100117 117 #42: 100031 31 99393 607 #43: 100077 77 99612 388 #44: 99949 51 99670 330 #45: 100003 3 99872 128 #46: 99615 385 99650 350 #47: 100224 224 100229 229 #48: 99814 186 99873 127 #49: 100439 439 99557 443 #50: 100093 93 100069 69 #51: 100066 66 99829 171 #52: 100189 189 100475 475 Sum: 5200000 13362 5200000 12812 Difference between deviations: 4,20% Elapsed time Rnd: 00:00:00.2086495 Elapsed time Rng: 00:00:00.4808033
Conclusions and recommendation
- Both methods are quite fast given the amount of generated numbers, but Random is more than twice as fast!
- RandomNumberGenerator doesn't always have a better distribution than Random, it varies. The difference between the total deviations looks to be around 10% even though there are some outliers, but I didn't measure that. It doesn't look like one method is statistically better than the other.
- Random is more flexible, because you can use a seed if you need to. You can also use Random.Shared to use a thread safe instance.
Given the speed and flexibility of Random, I would recommend Random over RandomNumberGenerator.