【问题标题】:What is the probability of collision with a 6 digit random alphanumeric code?与 6 位随机字母数字代码发生冲突的概率是多少?
【发布时间】:2011-11-27 07:31:45
【问题描述】:

我正在使用以下 perl 代码生成随机字母数字字符串(仅限大写字母和数字),以用作我的 MySQL 数据库中记录的唯一标识符。数据库可能保持在 1,000,000 行以下,但绝对现实的最大值约为 3,000,000。我有 2 条记录具有相同随机代码的危险机会,还是可能发生的次数很少?我对概率知之甚少(如果从这个问题的性质来看,这还不是很清楚的话),并且希望有人能提供意见。

perl -le 'print map { ("A".."Z", 0..9)[rand 36] } 1..6'

【问题讨论】:

  • 为什么不能只使用自动增量字段?
  • 如果由于某种原因自动增量不起作用,请考虑改用 UUID。这些被设计为具有最小碰撞机会的随机标识符。 metacpan.org/module/Data::UUID
  • 如果您在数据库中创建唯一索引,当发生冲突时您会从 DBI 获得异常。您可以捕获异常,生成另一个代码,然后再次尝试插入。当然,当您使用许多可用代码时,这不会特别有效。

标签: mysql perl hash probability alphanumeric


【解决方案1】:

由于Birthday Paradox,它的可能性比您想象的要大。

有 2,176,782,336 个可能的代码,但即使只插入 50,000 行,发生冲突的可能性也很高。对于 1,000,000 行,几乎不可避免地会发生很多冲突(我认为平均约为 250)。

我进行了一些测试,这是在第一次碰撞发生之前我可以生成的代码数量:

  • 73366
  • 59307
  • 79297
  • 36909

随着代码数量的增加,冲突将变得更加频繁。

这是我的测试代码(用 Python 编写):

>>> import random
>>> codes = set()
>>> while 1:
        code=''.join(random.choice('1234567890qwertyuiopasdfghjklzxcvbnm')for x in range(6))
        if code in codes: break
        codes.add(code)

>>> len(codes)
36909

【讨论】:

    【解决方案2】:

    嗯,你有 36**6 个可能的代码,大约是 20 亿。称之为d。使用找到的公式here,我们发现 n 个代码的冲突概率约为

    1 - ((d-1)/d)**(n*(n-1)/2)

    对于任何超过 50,000 左右的 n,这是相当高的。

    看起来 10 个字符的代码的冲突概率只有 1/800 左右。所以选择 10 个或更多。

    【讨论】:

      【解决方案3】:

      根据http://en.wikipedia.org/wiki/Birthday_paradox#Approximation_of_number_of_people 中给出的方程式,在这种大小的宇宙中仅插入大约 55,000 条记录后,有 50% 的机会遇到至少一次碰撞:

      http://wolfr.am/niaHIF

      尝试插入两到六倍的记录几乎肯定会导致冲突。您需要非随机分配代码,或使用更大的代码。

      【讨论】:

        【解决方案4】:

        如前所述,生日悖论使这一事件很有可能发生。特别是,当问题被视为碰撞问题时,可以确定准确的近似值。设p(n; d) 是至少两个数字相同的概率,d 是组合数,n 是轨迹数。然后,我们可以证明p(n; d) 约等于:

        1 - ((d-1)/d)^(n*(n-1)/2)
        

        我们可以很容易地在 R 中绘制它:

        > d = 2176782336
        > n = 1:100000
        > plot(n,1 - ((d-1)/d)^(n*(n-1)/2), type='l')
        

        给了

        如您所见,碰撞概率随着试验/行数的增加而迅速增加

        【讨论】:

          【解决方案5】:

          虽然我不知道您希望如何使用这些伪随机 ID 的具体细节,但您可能需要考虑生成一个包含 3000000 个整数(从 1 到 3000000)的数组并随机打乱它。这将保证这些数字是唯一的。 见Fisher-Yates shuffle on Wikipedia

          【讨论】:

          • 考虑到可能的排列数量,某个序列比其他序列更有可能被选中。
          【解决方案6】:

          注意事项:谨防依赖内置的rand,其中伪随机数生成器的质量很重要。我最近发现了Math::Random::MT::Auto

          Mersenne Twister 是一种快速伪随机数生成器 (PRNG),能够为可能耗尽可用“真正”随机数据源或系统的应用程序提供大量 (> 10^6004)“高质量”伪随机数据 -提供了 PRNG,例如 rand

          该模块提供了rand 的替代品,非常方便。

          您可以使用以下代码生成键序列:

          #!/usr/bin/env perl
          
          use warnings; use strict;
          use Math::Random::MT::Auto qw( rand );
          
          my $SEQUENCE_LENGTH = 1_000_000;
          my %dict;
          my $picks;
          
          for my $i (1 .. $SEQUENCE_LENGTH) {
              my $pick = pick_one();
              $picks += 1;
          
              redo if exists $dict{ $pick };
          
              $dict{ $pick } = undef;
          }
          
          printf "Generated %d keys with %d picks\n", scalar keys %dict, $picks;
          
          sub pick_one {
              join '', map { ("A".."Z", 0..9)[rand 36] } 1..6;
          }
          

          前段时间,我写过limited range of built-in rand on Windows。您可能不在 Windows 上,但您的系统可能存在其他限制或陷阱。

          【讨论】:

            猜你喜欢
            • 2011-03-02
            • 2015-07-29
            • 1970-01-01
            • 2012-09-03
            • 2019-08-11
            • 1970-01-01
            • 1970-01-01
            • 2017-09-29
            • 1970-01-01
            相关资源
            最近更新 更多