生成两位数不同的唯一代码

时间:2011-03-21 15:56:24

标签: mysql algorithm perl performance optimization

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

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

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

  1. 从MySQL表中检索所有以前的代码。
  2. 一次生成一个代码。
  3. 使用之前的所有代码减去生成的代码。
  4. 检查减法结果中的非零数字。
  5. 如果是> 1,接受生成的代码并将其添加到以前的代码中。
  6. 否则,请跳至2.
  7. 对于请求的代码数重复步骤2到6。
  8. 将生成的代码保存在数据库表中。
  9. 该算法工作正常,但问题与性能有关。在请求生成大量代码时需要很长时间才能生成代码,例如:10,000。

    问题:有没有办法改善此算法的性能?

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

5 个答案:

答案 0 :(得分:10)

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

在CPAN中查看Algorithm :: LUHN以实现perl实现。

答案 1 :(得分:3)

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

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;
}

答案 2 :(得分:2)

将数字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。由于每个数字和7位数有8个其他选项,因此您只需要检查56个可能的值。检查哈希表中是否有任何值。如果您尚未使用任何这些值,则可以使用随机节点。如果您使用过任何一种,那么就不能。
  3. 从扩充树中删除您的节点。确保维护增强信息。
  4. 如果您无法使用该值,请返回步骤1.
  5. 如果您可以使用该值,则会有一个新的随机代码。将其添加到哈希表中。
  6. 现在,检查值是否可用需要O(1)时间而不是O(n)时间。另外,找到另一个可用的随机值来检查需要O(log n)时间而不是......啊......我不确定如何分析你的算法。

    简而言之,如果从头开始并使用此算法,您将在O(n log n)中生成完整的有效代码列表。因为n是10,000,000,所以需要几秒钟或者什么。

    我是否在每个人那里做数学计算?如果不检查或者我是否需要澄清任何内容,请告诉我。

答案 3 :(得分:1)

使用哈希。

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

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

答案 4 :(得分:0)

Howabout:

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

Presto,保证两位数不同。 :d

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