我注意到c#中随机数生成存在一个奇怪的问题,看起来集合(模式)的重复次数比预期的要多得多。
我正在编写一种生成激活码的机制,一系列7个数字(范围0-29)。 进行数学计算,应该有30 ^ 7(220亿)种可能的激活码组合。基于此,在生成第十亿个代码之前,应该极不可能获得重复的激活码。然而,运行我的测试,我开始在大约60,000次迭代后获得重复的代码,这是非常令人惊讶的。我也尝试过使用具有类似结果的RNGCryptoServiceProvider,我在大约100,000次迭代时得到重复。
我真的想知道这是否是.Net中随机数生成的错误/限制,或者我是否做错了。
以下代码是验证生成代码唯一性的测试:
static void Main(string[] args)
{
Random rand = new Random();
RandomActivationCode(rand, true);
Console.Out.WriteLine("Press enter");
Console.ReadLine();
}
static void RandomActivationCode(Random randomGenerator)
{
var maxItems = 11000000;
var list = new List<string>(maxItems);
var activationCodes = new HashSet<string>(list);
activationCodes.Clear();
DateTime start = DateTime.Now;
for (int i = 0; i < maxItems; ++i)
{
string activationCode = "";
for (int j = 0; j < 7; ++j)
{
activationCode += randomGenerator.Next(0,30) + "-";
}
if (activationCodes.Contains(activationCode))
{
Console.Out.WriteLine("Code: " + activationCode);
Console.Out.WriteLine("Duplicate at iteration: " + i.ToString("##,#"));
Console.Out.WriteLine("Press enter");
Console.ReadLine();
Console.Out.WriteLine();
Console.Out.WriteLine();
}
else
{
activationCodes.Add(activationCode);
}
if (i % 100000 == 0)
{
Console.Out.WriteLine("Iteration: " + i.ToString("##,#"));
Console.Out.WriteLine("Time elapsed: " + (DateTime.Now - start));
}
}
}
我的解决方法是使用10个数字激活码,这意味着测试运行时不会生成任何重复值。该测试最多可运行1100万次迭代(此后内存耗尽)。
答案 0 :(得分:3)
这一点都不奇怪;这正是你应该期待的。当可能性空间很大时,你认为应该花费很长时间来产生重复的信念是完全错误的,所以停止相信。开始相信真相:如果有n个可能的代码,那么你应该开始在生成的n个代码的平方根处获得重复,如果n是220亿,则大约为15万。
以这种方式思考:当你生成root-n代码时,他们中的大多数已经有一个根本n-in-n机会发生冲突。将root-n乘以大致root-n-in-n,你得到......大约100%的碰撞几率。
这当然不是一个严格的论点,但它应该给你正确的直觉,以取代你错误的信念。如果这个论点不能令人信服,那么你可能想阅读我关于这个主题的文章:
http://blogs.msdn.com/b/ericlippert/archive/2010/03/22/socks-birthdays-and-hash-collisions.aspx
如果要生成唯一代码,则生成GUID ;这就是他们的目的。 请注意,GUID不保证是随机的,只保证是唯一的。
生成随机看似代码的另一个选择是生成数字1,2,3,4,...,这些代码根本不是随机的,但是是唯一的... ,然后使用乘法逆技术对这些数字进行随机查找的唯一编码。有关详细信息,请参阅http://ericlippert.com/2013/11/14/a-practical-use-of-multiplicative-inverses/。