【问题标题】:Algorithms: random unique string算法:随机唯一字符串
【发布时间】:2011-01-17 21:14:36
【问题描述】:

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

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

生成后我会将它们存储在数据库中(它们将分配给其他实体)。

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

  1. 生成 2 个从 0 到 9 的随机值——它们将用于字符串中的数字;
  2. 从 0 到 25 生成 6 个随机值,并将它们添加到 64——它们将用作 6 个符号;
  3. 将所有内容连接成一个字符串;
  4. 检查字符串是否已经存在于数据库中;如果没有,请重复。

我对该算法的担忧是它不能保证在有限时间内得到结果(如果数据库中已经有很多值)。

问题:您能否就如何改进该算法使其更具确定性提供建议?

谢谢。

【问题讨论】:

  • 对于初学者,您需要计算出从这些组合中获得了多少可能性。
  • 数据库中的值...它们也遵循这种模式吗?你知道生成它们的论坛吗?你能在数据库的字符串中找到一个模式吗?否则,您将始终有碰撞的风险......
  • 为什么需要随机性?为什么不能使用顺序字符串?
  • 这很难同时保持高斯分布。
  • 我展示了大约 240 亿种可能的组合。由于生日悖论,您将在大约平方根或大约 15 万处开始遇到碰撞问题。

标签: algorithm random uniqueidentifier


【解决方案1】:
  1. 应该是唯一的字符串;
  2. 字符串长度应为 8 个字符;
  3. 应该包含2位数字;
  4. 所有符号(非数字字符)- 应为大写。

假设:

  • 要求 #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 个随机字母放在其余位置。

两个不同的整数有 28 种可能性((8*8 - 8 个重复项)/2 次排序),字母有 266 种可能性,数字有 100 种可能性,总共 #有效组合为 Ncomb = 864964172800 = 8.64 x 1011


编辑:如果您想避免使用数据库进行存储,但仍要保证字符串的唯一性并且让它们在密码学上是安全的,那么您最好的选择是密码学上的随机从 0 和 Nmaxcomb 之间的计数器到可能输出字符串空间的子集的双射。 (Bijection 表示输出字符串和输入计数器一一对应。)

Feistel networks 可以做到这一点,它通常用于散列函数和对称加密(包括 AES)。您可能希望选择 Nmax = 239 这是 2 comb 的最大幂,并使用 39-比特Feistel网络,使用你保密的常量密钥。然后将计数器插入 Feistel 网络,得到另一个 39 位数字 X,然后将其转换为相应的字符串,如下所示:

  1. 重复以下步骤 6 次:
  2. 取 X mod 26,生成一个大写字母,并设置 X = X / 26。
  3. 取 X mod 100 生成两位数,并设置 X = X / 100。
  4. X 现在将介于 0 和 17 之间(239 / 266 / 100 = 17.796...)。将此数字映射到两个唯一的数字位置(使用查找表可能最简单,因为我们只讨论 28 种可能性。如果您有更多,请使用 Floyd's algorithm for generating a unique permutation,并使用 mod + 整数除法的可变基数技术而不是生成一个随机数)。
  5. 遵循上述随机方法,但改用此算法生成的数字。

或者,使用 40 位数字,如果您的 Feistel 网络的输出为 > Ncomb,则增加计数器并重试。这覆盖了整个字符串空间,代价是拒绝无效数字并不得不重新执行算法。 (但您不需要数据库来执行此操作。)

但除非你知道自己在做什么,否则这不是一件容易的事。

【讨论】:

  • 您假设他能够在字符串中分配数字 - 但他说字符串需要以两个数字开头,后跟 6 个“符号”。
  • @Kirk:不,他没有。他说他的字符串需要包含两个数字,他的意图是连接两个数字和6个符号。没有明确说明这是因为这两个数字需要先出现,还是它们可以在任何地方,但他把它们放在首位,因为这样做很容易。
  • (就此而言,他没有具体说明串联是数字后跟字母,还是字母后跟数字,还是其他。)
  • 对不起,我的立场是正确的,我一定是误读或假设了。
【解决方案2】:

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

  1. 您必须避免 0/O 和 I/1,它们很容易被误认为是彼此。
  2. 您必须避免太多连续的字母,这可能会拼出粗鲁的单词。

就 2 而言,您可以通过使用 LLNLLNLL 作为您的模式来避免该问题(L = 字母,N = 数字)。

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

【讨论】:

  • 要小心,如果你花太多时间避免粗鲁的话,你不会变成这样:thedailywtf.com/Articles/The-Automated-Curse-Generator.aspx
  • 密码通常不必是唯一的。 “对不起,具有该密码的用户已经存在”:)
  • 它将是“登录”而不是密码:)
  • @Budda:所以你必须小心你生成的内容。
【解决方案3】:

我在您的要求中没有看到任何说明字符串需要是随机的。你可以做类似下面的伪代码:

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

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

如果您需要随机生成的字符串,那么您需要保留之前生成的字符串的某种记录,因此您将不得不访问数据库(或将它们全部保存在内存中,或将它们写入到一个文本文件)并检查该列表。

【讨论】:

    【解决方案4】:

    我认为您可以安全地使用数以万计的此类 ID,即使在那之后您也很可能会没事。

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

    我愿意打赌,尽管您在有生之年永远不会看到额外的功能发挥作用:)

    【讨论】:

    • 多少钱? Budda 估计需要 100 万根琴弦,而这套琴弦共有 250 万根。这不是一个巨大的安全边际!
    • 好吧,我开始写的时候还没到 :)
    • 佛陀写的是:10^2*25^6 = 2 441 406 250 that is almost 2.5M。这里有两个错误:(i)应该是26^6; (ii) 2 441 406 250 几乎是 25 亿。所以我收回我的评论:-)
    • 是的,数十亿,我的错误 :) 十亿。
    【解决方案5】:

    反过来做:生成一个大的随机数,然后将其拆分以获得单个字符:

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

    等等。

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

    但是,当您尝试插入一个新值并且它已经在数据库中时,您可以轻松找到比最初生成的数字更大的最小未分配数字,并使用它来代替您生成的那个。

    您从这种方法中获得的好处是,即使大多数代码已经分配,​​您也可以保证相对较快地找到可用代码。

    【讨论】:

      【解决方案6】:

      一方面,您的要求列表并未说明字符串必须是随机的,因此您可以考虑使用数据库索引之类的东西。

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

      1. 将字符串作为数字存储在数据库中。不确定这会在多大程度上提高性能。
      2. 根本不存储使用过的字符串。您可以使用上面的“索引”方法,但以看似随机的方式将整数转换为字符串(例如,使用位移)。没有太多的研究,没有人会注意到模式。

      例如,如果我们有序列 1, 2, 3, 4, ... 并使用循环二进制右移 1 位,它将变成 4, 1, 5, 2, ...(假设我们只有 3 位) 它也不必是一个转变,它可以是一个排列或任何其他“随机化”。

      【讨论】:

        【解决方案7】:

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

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

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-11-21
          • 2010-10-18
          • 2014-10-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多