【问题标题】:Random number distribution is uneven / non uniform随机数分布不均匀/不均匀
【发布时间】:2014-07-27 14:11:49
【问题描述】:

我注意到 c# 中随机数生成的一个奇怪问题,看起来集合(模式)的重复次数比您预期的要多。

我正在编写一种生成激活码的机制,即一系列 7 个数字(范围 0-29)。 算一下,应该有 30^7(220 亿)个可能的激活码组合。基于此,在生成第 10 亿个代码之前获得重复激活码的可能性极小。然而运行我的测试,我在大约 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 万次迭代(之后会耗尽内存)。

【问题讨论】:

  • 30^7 (22billion) possible combinations ... extremely unlikely to get a duplicate [...]before the 1 billionth code... - 生日悖论?
  • 为什么会随机生成激活码?即使这样,您仍然必须存储它们,以便您可以检查激活。所以你可以随时检查一个是否已经存在,如果是,则生成新的。
  • @ErnodeWeerd 同意,我在帖子中提到了我使用 10 个字符的激活码作为解决方法

标签: c# .net random


【解决方案1】:

这一点也不奇怪;这正是您应该期待的。您认为当可能性空间很大时需要很长时间才能生成重复项的想法是完全错误的,所以别再相信了。开始相信事实:如果有 n 个可能的代码,那么您应该在生成的 n 个代码的平方根处开始获得重复,如果 n 是 220 亿,则大约是 15 万。

这样想:当您生成 root-n 代码时,它们中的大多数已经大致有一个 root-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/

【讨论】:

  • 代码是随机的很重要,这样它在密码学上是安全的。碰撞不是一个问题,但如果它发生,则不受欢迎。我已经考虑过解决方法,例如查询数据库并在发生冲突时重新创建激活码。
  • @setebos:你说“加密安全”——安全防止什么攻击
  • 别再相信粗体字了 - 我想你可以把它放在 T 恤上 ;-)
  • @EricLippert 您可以使用激活码在网站上注册,因此您要避免有人猜测用户的激活码。
  • @setebos:那么 220 亿个代码已经太少了。如果你有 10 亿用户,那么每 22 个代码中就有一个是好的!
猜你喜欢
  • 2020-04-04
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
相关资源
最近更新 更多