【问题标题】:Generating unique codes that are different in two digits生成两位数不同的唯一代码
【发布时间】:2011-07-19 19:24:39
【问题描述】:

我想生成唯一的代码编号(完全由 7 位数字组成)。码号随机生成并保存在 MySQL 表中。

我还有一个要求。所有生成的代码应至少相差两位数。这对于在键入用户代码时防止错误很有用。希望它可以防止在执行某些操作时引用另一个用户代码,因为它不太可能丢失两位数并匹配另一个现有用户代码。

生成算法的工作原理如下:

  1. 从 MySQL 表中检索所有以前的代码(如果有)。
  2. 一次生成一个代码。
  3. 用所有之前的代码减去生成的代码。
  4. 检查减法结果中的非零位数。
  5. 如果大于 1,则接受生成的代码并将其添加到之前的代码中。
  6. 否则,跳转到 2。
  7. 重复步骤 2 到 6 以获得请求的代码数量。
  8. 将生成的代码保存在数据库表中。

算法运行良好,但问题与性能有关。请求生成大量代码时,生成代码需要很长时间,例如:10,000。

问题:有没有办法提高这个算法的性能?

如果重要的话,我在 Ubuntu 服务器上使用 perl + MySQL。

【问题讨论】:

    标签: mysql algorithm perl performance optimization


    【解决方案1】:

    您是否考虑过 Luhn 算法的变体? Luhn 用于为许多应用程序中的数字字符串生成校验位,包括信用卡帐号。它是生成标识符的 ISO-7812-1 标准的一部分。它将捕获输入的任何一个错误数字,这意味着任何两个有效数字至少有两个数字不同。

    查看 CPAN 中的 Algorithm::LUHN 以了解 perl 实现。

    【讨论】:

    • 这真是个好主意。它还确保没有两个代码只能改变一个数字,因为更改任何数字也会更改校验和。
    • 更正 - 更改数字将可能更改校验和。如果您尝试过,您绝对可以创建两个仅相差一个字符的代码。
    • 单个数字错误(一个错误的数字没有换位)将始终被捕获。每个数字都为校验和(0,1,2,3,4,5,6,7,8,9 或 0,2,4,6,8,1,3,5 ,7,9 取决于是在偶数还是奇数位置)。如果数字发生变化,则总校验和将更改某个等于或小于 9 的非零值,这保证了总校验和 mod 10 也会发生变化。
    • 如果某些数字被调换,两个数字可以共享除一个数字之外的所有数字。
    • 所以,我需要做的就是生成任何随机的六位数代码并附加算法生成的校验和数字。就这样。非常感谢!
    【解决方案2】:

    不检索现有代码,只生成一个潜在的新代码,看看数据库中是否有冲突的代码:

    SELECT code FROM table WHERE abs(code-?) regexp '^[1-9]?0*$';
    

    (其中占位符是新生成的代码)。

    啊,我错过了一次生成大量代码的部分。这样做(完全未经测试):

    my @codes = existing_codes();
    
    my $frontwards_index = {};
    my $backwards_index = {};
    for my $code (@codes) {
        index_code($code, $frontwards_index);
        index_code(reverse($code), $backwards_index);
    }
    
    my @new_codes = map generate_code($frontwards_index, $backwards_index), 1..10000;
    
    sub index_code {
        my ($code, $index) = @_;
        push @{ $index{ substr($code, 0, length($code)/2) } }, $code;
        return;
    }
    
    sub check_index {
        my ($code, $index) = @_;
        my $found = grep { ($_ ^ $code) =~ y/\0//c <= 1 } @{ $index{ substr($code, 0, length($code)/2 } };
        return $found;
    }
    
    sub generate_code {
        my ($frontwards_index, $backwards_index) = @_;
    
        my $new_code;
        do {
            $new_code = sprintf("%07d", rand(10000000));
        } while check_index($new_code, $frontwards_index)
            || check_index(reverse($new_code), $backwards_index);
        index_code($new_code, $frontwards_index);
        index_code(reverse($new_code), $backwards_index);
        return $new_code;
    }
    

    【讨论】:

    • Regex 搜索无法被索引,因此这仅比现有解决方案略好。
    • @Nick Johnson:让服务器读取所有现有数据和让服务器返回所有现有数据之间有很大的区别。但我的 SQL 解决方案实际上并没有帮助,因为需要许多新代码。
    • 存在常数因子差异。这意味着它仍然无法很好地扩展。
    【解决方案3】:

    将数字 0 到 9,999,999 放入扩充二叉搜索树中。增强是跟踪左侧和右侧的子节点数量。因此,例如,当您的算法开始时,顶部节点的值应为 5,000,000,并且它应该知道左侧有 5,000,000 个节点,右侧有 4,999,999 个节点。现在创建一个哈希表。对于您已经使用的每个值,从增强二叉搜索树中删除其节点并将该值添加到哈希表中。确保保持增强。

    要获取单个值,请按以下步骤操作。

    1. 使用顶部节点来确定树中还剩下多少节点。假设您还剩下 n 个节点。在 0 和 n 之间选择一个随机数。使用扩充,您可以在 log(n) 时间内找到树中的第 n 个节点。
    2. 找到该节点后,计算会使该节点上的值无效的所有值。假设您的节点的值为 1,111,111。如果您已经有 2,111,111 或 3,111,111 或...,那么您不能使用 1,111,111。由于每个数字有 8 个其他选项和 7 个数字,您只需要检查 56 个可能的值。检查这些值中是否有任何值在您的哈希表中。如果您尚未使用任何这些值,则可以使用随机节点。如果您使用过其中任何一个,则不能。
    3. 从扩充树中删除您的节点。确保您维护增强的信息。
    4. 如果您不能使用该值,请返回步骤 1。
    5. 如果您可以使用该值,您就有了一个新的随机码。将其添加到哈希表中。

    现在,检查一个值是否可用需要 O(1) 时间而不是 O(n) 时间。此外,找到另一个可用的随机值来检查需要 O(log n) 时间而不是...啊...我不确定如何分析您的算法。

    长话短说,如果您从头开始并使用此算法,您将在 O(n log n) 中生成完整的有效代码列表。由于 n 是 10,000,000,因此需要几秒钟或其他时间。

    每个人我都在那里做数学吗?让我知道如果这不检查或者我是否需要澄清任何事情。

    【讨论】:

      【解决方案4】:

      使用哈希。

      生成成功后的代码(不与任何现有代码冲突),但将该代码放入哈希表中,并将其他 63 个相差恰好一位数的代码放入哈希中。

      要查看随机生成的代码是否会与现有代码冲突,只需检查该代码是否存在于哈希中。

      【讨论】:

        【解决方案5】:

        怎么样:

        通过自动递增前一个代码生成一个 6 位代码。 通过增加前一个 mod 10 来生成 1 位代码。 将两者连接起来。

        Presto,保证相差两位数。 :D

        (是的,有点滑稽。我假设“随机”或至少是准随机是必要的。在这种情况下,生成一个 6 位随机密钥,重复直到它不重复(即使列唯一,重复直到插入没有失败约束),然后生成一个校验位,正如有人已经说过的那样。)

        【讨论】:

          猜你喜欢
          • 2013-05-04
          • 2017-06-17
          • 1970-01-01
          • 1970-01-01
          • 2011-08-19
          • 2020-06-21
          • 2012-04-05
          • 2019-10-09
          • 1970-01-01
          相关资源
          最近更新 更多