Boris Eetgerink

March 9, 2023

C#: Random vs. RandomNumberGenerator

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:

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.

About Boris Eetgerink

Hey, this is my C# .NET blog. I don't have a comments system, but you can contact me at boriseetgerink at hey dot com.