算法:随机唯一字符串

时间:2011-01-17 21:14:36

标签: algorithm random uniqueidentifier

我需要生成符合以下要求的字符串:

  1. 它应该是一个唯一的字符串;
  2. 字符串长度应为8个字符;
  3. 它应包含2位数字;
  4. 所有符号(非数字字符)应为大写。
  5. 我会在生成后将它们存储在数据库中(它们将被分配给其他实体)。

    我的意图是做这样的事情:

    1. 从0到9生成2个随机值 - 它们将用于字符串中的数字;
    2. 从0到25生成6个随机值并将它们添加到64 - 它们将用作6个符号;
    3. 将所有内容连接成一个字符串;
    4. 检查数据库中是否已存在该字符串;如果不是 - 重复。
    5. 我对该算法的关注是它不能保证有限时间内的结果(如果数据库中已有很多值)。

      问题:请您就如何改进此算法提供更具确定性的建议?

      感谢。

7 个答案:

答案 0 :(得分:6)

  
      
  1. 它应该是唯一的字符串;
  2.   
  3. 字符串长度应为8个字符;
  4.   
  5. 它应包含2位数字;
  6.   
  7. 所有符号(非数字字符) - 应为大写。
  8.   

假设:

  • 要求#2和#3是准确的(恰好是8个字符,正好是2个数字),而不是最小值
  • 要求#4中的“符号”是26个大写字母A到Z
  • 你想要一个均匀分布的随机字符串

然后您提出的方法有两个问题。一个是字母A - Z是ASCII 65 - 90,而不是64 - 89.另一个是它不会在可能的字符串空间内均匀分布数字。这可以通过执行以下操作来解决:

  1. 生成0到7之间的两个不同的整数,并对它们进行排序。
  2. 从0到9生成2个随机数。
  3. 从A到Z生成6个随机字母。
  4. 在步骤#1中使用两个不同的整数作为位置,并将2个数字放在这些位置。
  5. 将6个随机字母放在其余位置。
  6. 两种不同的整数有两种可能性((8 * 8 - 8次重复)/ 2次排序),26种 6 可能的字母,100种可能的数字,总数#有效组合为N comb = 864964172800 = 8.64 x 10 11


    编辑:如果您想避免数据库存储,但仍然保证字符串的唯一性使它们具有加密安全性,那么您最好的选择是加密随机从0和N max < = N comb 之间的计数器的双射到可能输出串的空间的子集。 (Bijection表示输出字符串和输入计数器之间存在一对一的对应关系。)

    Feistel networks可以实现这一点,它们通常用于散列函数和对称加密(包括AES)。您可能希望选择N max = 2 39 ,这是2< = N comb 的最大功率,并使用39 -bit Feistel网络,使用您保密的常量密钥。然后将计数器插入Feistel网络,然后输出另一个39位数字X,然后将其转换为相应的字符串,如下所示:

    1. 重复以下步骤6次:
    2. 取X mod 26,生成一个大写字母,然后设置X = X / 26。
    3. 取X mod 100生成两位数,然后设置X = X / 100.
    4. X现在介于0和17之间(2 39 / 26 6 / 100 = 17.796 ......)。将此数字映射到两个唯一的数字位置(可能最容易使用查找表,因为我们只讨论了28种可能性。如果您有更多,请使用Floyd's algorithm for generating a unique permutation,并使用mod + integer divide的变量基技术生成一个随机数)。
    5. 按照上面的随机方法,但请使用此算法生成的数字。
    6. 或者,使用40位数字,如果您的Feistel网络的输出是> N comb ,然后递增计数器并再试一次。这将覆盖整个字符串空间,代价是拒绝无效数字并且必须重新执行算法。 (但是您不需要数据库来执行此操作。)

      但除非你知道自己在做什么,否则这不是什么。

答案 1 :(得分:2)

这些用户密码是?如果是这样,您需要考虑以下几点:

  1. 您必须避免使用0 / O和I / 1,这很容易被误认为是彼此。
  2. 你必须避免使用太多连续的字母,这可能会说出一个粗鲁的字眼。
  3. 就2而言,您可以通过使用LLNLLNLL作为模式来避免此问题(L =字母,N =数字)。

    如果您需要一个25亿个池中的100万个密码,您肯定会在数据库中发生冲突,因此您必须优雅地处理它们。但是,如果您的随机数生成器很稳健,那么简单的重试就足够了。

答案 2 :(得分:0)

我没有在您的要求中看到任何声明该字符串必须是随机的。您可以执行以下伪代码:

for letters in ( 'AAAAAA' .. 'ZZZZZZ' ) {
  for numbers in ( 00 .. 99 ) {
    string = letters + numbers
  }
}

这将创建八个字符长的唯一字符串,两个数字和六个大写字母。

如果你需要随机生成的字符串,那么你需要保留一些先前生成过哪些字符串的记录,这样你就不得不点击一个数据库(或者将它们全部留在内存中,或者写出来到文本文件)并检查该列表。

答案 3 :(得分:0)

我认为你对成千上万的这类ID很安全,甚至在那之后你很可能还好。

现在,如果你想要一些确定性,你可以在一定数量的失败后强制输入密码。在50次失败后说,你随机选择一个密码并将其中的一部分增加1,直到获得一个免费密码。

我愿意打赌,虽然你在生活中永远不会看到额外的功能:)

答案 4 :(得分:0)

首先,您的需求列表并未声明字符串必须是随机的,因此您可能会考虑类似数据库索引。

如果要求“随机”,您可以做一些改进。

  1. 将字符串存储为数据库中的数字。不确定这会改善多少性能。
  2. 根本不存储使用过的字符串。您可以采用上面的“索引”方法,但是以看似随机的方式将整数转换为字符串(例如,采用位移)。没有太多的研究,没有人会注意到模式。
  3. 例如,如果我们有序列1,2,3,4 ......并且使用循环二进制右移1位,它将变为4,1,5,2 ......(假设我们只有3位) 它也不一定是一种转变,它可以是一种排列或任何其他“随机化”。

答案 5 :(得分:0)

反过来说:生成一个大的随机数,你将拆分以获得单个字符:

 long bigrandom = ...;
 int firstDigit = bigRandom % 10;
 int secondDigit = ( bigrandom / 10 ) % 10;

等等。

然后,您只将随机数存储在数据库中,而不是字符串。由于字符串和数字之间存在一对一的关系,因此这并没有什么区别。

但是,当您尝试插入新值并且它已经在数据库中时,您可以轻松找到最小的未分配数字,而不是最初生成的数字,并使用它而不是您生成的数字。

您从这种方法中获得的是,即使已经分配了大部分代码,您仍可以相对快速地找到可用代码。

答案 6 :(得分:0)

你的方法的问题很明显,虽然你的记录很少,但你不太可能发生碰撞,但随着记录数量的增加,机会会增加,直到你发生碰撞的可能性增大。最终,在获得“有效”结果之前,您将遇到多次碰撞。每次都需要进行表扫描以确定代码是否有效,并且整个过程变得一团糟。

最简单的解决方案是预先计算您的代码

从第一个代码00AAAA开始,然后递增以生成00AAAB,00AAAC ... 99ZZZZ。以随机顺序将它们插入表中。当您需要新代码时,从表中检索到顶部记录未使用的记录(然后将其标记为已使用)。如上所述,它不是一张巨大的表 - 只有几百万条记录。

  • 您不需要计算任何随机数并为每个用户生成字符串(已完成)
  • 您无需检查是否已使用过任何内容,只需获取下一个
  • 即可
  • 在找到可用的东西之前没有机会发生多次碰撞。

如果您需要更多“代码”,只需生成一些“随机”字符串并将其附加到表格中。