随机数分布不均匀/不均匀

时间:2014-07-27 14:11:49

标签: c# .net random

我注意到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万次迭代(此后内存耗尽)。

1 个答案:

答案 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/