using System.Security.Cryptography;
using System.Collections.Generic;
const int REPETITIONS = 1000000;
static void Main(string[] args)
Console.WriteLine("Original BIASED implementation");
PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKeyOriginal_BIASED);
Console.WriteLine("Updated implementation");
PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKey);
static void PerformTest(int repetitions, int keySize, Func<int, string> generator)
Dictionary<char, int> counts = new Dictionary<char, int>();
foreach (var ch in KeyGenerator.chars) counts.Add(ch, 0);
for (int i = 0; i < REPETITIONS; i++)
var key = generator(KEY_SIZE);
foreach (var ch in key) counts[ch]++;
int totalChars = counts.Values.Sum();
foreach (var ch in KeyGenerator.chars)
Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
public class KeyGenerator
internal static readonly char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
public static string GetUniqueKey(int size)
byte[] data = new byte[4*size];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
var rnd = BitConverter.ToUInt32(data, i * 4);
var idx = rnd % chars.Length;
result.Append(chars[idx]);
return result.ToString();
public static string GetUniqueKeyOriginal_BIASED(int size)
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
byte[] data = new byte[size];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
StringBuilder result = new StringBuilder(size);
result.Append(chars[b % (chars.Length)]);
return result.ToString();