【问题标题】:Generating unique 6 digit code生成唯一的 6 位代码
【发布时间】:2013-05-04 14:00:04
【问题描述】:

我正在从以下字符生成一个 6 位代码。这些将用于在贴纸上盖章。
它们将以 10k 或更少的批次生成(打印前),我不认为总数会超过 1-200 万(可能要少得多)。
生成这批代码后,我会检查现有代码的MySQL数据库,确保没有重复。

// exclude problem chars: B8G6I1l0OQDS5Z2

$characters = 'ACEFHJKMNPRTUVWXY4937';

$string = '';

for ($i = 0; $i < 6; $i++) {
    $string .= $characters[rand(0, strlen($characters) - 1)];
}   

return $string;
  1. 这是生成代码的可靠方法吗?
  2. 会有多少种可能的排列? (来自 21 个字符池的 6 位代码)。抱歉,数学不是我的强项

【问题讨论】:

  • 这里有一些关于各种方法的好帖子:stackoverflow.com/questions/1846202/…
  • 如果您不需要超过 800 万个代码,并且简短且明确的代码是高优先级,我会说您有一个很好的方法。

标签: php algorithm random permutation


【解决方案1】:

或者...您可以在 md5 中对用户名+日期时间进行编码并保存到数据库,这肯定会生成一个唯一的代码;)

【讨论】:

    【解决方案2】:

    21^6 = 85766121 种可能性。

    使用数据库并存储使用过的值是不好的。如果您想伪造随机性,可以使用以下方法:

    减少到 19 个可能的数字,并利用 p 是奇素数的 p^k 阶群总是循环的这一事实。

    取 7^19 阶的组,使用生成器互素到 7^19(我会选择 13^11,你可以选择任何不能被 7 整除的东西)。

    然后以下工作:

    $previous = 0;
    
    function generator($previous)
    {
    
      $generator = pow(13,11);
      $modulus = pow(7,19); //int might be too small
      $possibleChars = "ACEFHJKMNPRTUVWXY49";
    
      $previous = ($previous + $generator) % $modulus;
      $output='';
      $temp = $previous;
    
      for($i = 0; $i < 6; $i++) {
        $output += $possibleChars[$temp % 19];
        $temp = $temp / 19;
      }
    
      return $output;
    }
    

    它将循环遍历所有可能的值,并且看起来有点随机,除非他们去挖掘。一个更安全的选择是乘法组,但我已经忘记了我的数学:(

    【讨论】:

    • 生成的代码需要存储,因为它们与印章(产品)相关联,印章(产品)将具有与其相关的信息。如果我生成 10k 的代码,然后一个月后又生成 10k,我需要以某种方式检查这个新的 10k 批次中没有重复。
    • 我已经发布了如何忽略重复项,但如果您要存储其他信息,请忽略此答案。
    • @Jean-BernardPellerin php-ized 你的代码,希望你不介意,请检查是否一切正常
    • 每次都是唯一的还是我需要手动检查?而且我不希望它仅来自 21 个字符,对我来说什么都可以。
    • 6^19 还是 19^6? 6 不是一个奇怪的素数,所以我假设你的意思是后者。在这种情况下,您选择的生成器不是它的共质 (19^11) - 11^19 看起来还可以。我一直在寻找 5 个字符的代码并得到了这个答案;我使用 23^5(从字母表中删除了 I、O 和 Q)作为模数,使用 19^11 作为生成器,这似乎对我有用。
    【解决方案3】:

    我遇到了同样的问题,我发现了非常令人印象深刻的开源解决方案:

    http://www.hashids.org/php/

    您可以使用它,也值得查看它的源代码以了解幕后发生的事情。

    【讨论】:

      【解决方案4】:
      • 有很多可能的组合有或没有重复,所以你的逻辑就足够了
      • 冲突会很频繁,因为您使用的是rand,请参阅str_shuffle and randomness
      • rand 更改为mt_rand
      • 检查时使用memcachedredis 等快速存储而不是MySQL

      全部可能性

      21 ^ 6 = 85,766,121
      

      85,766,121 应该没问题,将数据库添加到这一代尝试:

      例子

      $prifix = "stamp.";
      
      $cache = new Memcache();
      $cache->addserver("127.0.0.1");
      
      $stamp = myRand(6);
      while($cache->get($prifix . $stamp)) {
          $stamp = myRand(6);
      }
      echo $stamp;
      

      使用的功能

      function myRand($no, $str = "", $chr = 'ACEFHJKMNPRTUVWXY4937') {
          $length = strlen($chr);
          while($no --) {
              $str .= $chr{mt_rand(0, $length- 1)};
          }
          return $str;
      }
      

      【讨论】:

      • 谢谢。 86磨就足够了。我将使用您对 mt_rand 的建议。我不确定如何使用 memcached 来检查新生成的批次中的代码是否已存在于 MySQL 中(包含所有先前生成的代码批次)。出于打印目的,我想象一次生成 10k 左右的代码
      【解决方案5】:

      您将拥有 21 ^ 6 个代码 = 85 766 121 ~ 85.8 百万个代码!

      要全部生成它们(这需要一些时间),请查看此问题的选定答案:algorithm that will take numbers or words and find all possible combinations

      【讨论】:

      • 你混淆了你的底数和指数,并且破坏了你的数字分隔,它是〜8600万
      【解决方案6】:

      正如 Baba 所说,即时生成字符串会导致大量碰撞。您越接近已经生成的 8000 万个字符串,获取可用字符串就越困难

      另一种解决方案可能是一次生成所有可能的组合,并将它们中的每一个都存储在数据库中,并带有一些布尔列字段来标记是否已使用行/令牌

      然后得到其中一个

      SELECT * FROM tokens WHERE tokenIsUsed = 0 ORDER BY RAND() LIMIT 0,1
      

      然后将其标记为已使用

      UPDATE tokens SET tokenIsUsed = 1 WHERE token = ...
      

      【讨论】:

      • 谢谢,我原以为如果存储 80 个磨机组合,在查询等方面会有相当大的性能损失。代码将被查找,而不仅仅是存储为日志。跨度>
      • 我认为您可以将这种方法与@Jean-Bernard 的循环生成相结合,让您的查询速度非常快 - 以某种随机顺序填充数据库中的所有组合,并且每次您需要新的值,您可以一次抓住它们。无论如何,可能值得一试。
      猜你喜欢
      • 2017-06-17
      • 2012-04-05
      • 2011-07-19
      • 2011-07-24
      • 2017-11-07
      • 2011-08-19
      • 1970-01-01
      • 2019-10-09
      • 1970-01-01
      相关资源
      最近更新 更多