【问题标题】:Generating a unique, but apparently random, identifier without loop+retry生成唯一但显然是随机的标识符,无需循环+重试
【发布时间】:2013-03-22 20:38:17
【问题描述】:

我正在尝试使用一种算法来为我的 rails 应用程序中的模型生成唯一的非顺序标记。

例如:

MyModel.create.token #=> '183685'
MyModel.create.token #=> '456873'
MyModel.create.token #=> '813870'

我能想到的唯一方法是创建一个随机的东西,检查冲突,然后重试。这对我来说似乎是一种很臭的代码,在某种程度上是一种力量。

class MyModel < ActiveRecord::Base
  before_create :set_token

  def set_token
    existing_model_count = nil

    # Loop while we have a token that already belongs to an existing model.
    while existing_model_count.nil? || existing_model_count > 0
      random_token = TokenGenerator.random_token
      existing_model_count = MyModel.where(token: random_token).count
    end

    # Loop exited, meaning we found an unused token.
    self.token = random_token
  end
end

有没有更好的方法来做到这一点,不涉及while 循环,该循环将迭代未知次数?


虽然这里的示例是 ruby​​,但这是一种适用于任何语言的通用算法问题,因此欢迎使用其他语言的解决方案。

【问题讨论】:

  • 您可以生成更长的令牌(例如 UUID)并假设它们很可能是唯一的。 stackoverflow.com/questions/39771/…en.wikipedia.org/wiki/Universally_unique_identifier
  • 遗憾的是,我正在寻找的位数远少于此。很容易在电话中写下来或陈述的事情。
  • 您可以持续枚举您的令牌范围内的所有有效令牌,然后让TokenGenerator.random_token 随机选择一个并将其删除。
  • 您是否提前知道您需要多少令牌(大约,也许)?或者您正在寻找一种完全开放的唯一令牌生成算法?

标签: ruby-on-rails ruby algorithm


【解决方案1】:

如果您的“令牌”是某个范围内的整数值,并且您事先知道令牌的数量,那么简单的 Bob Floyd 算法(在 Bentley 的“Programming Peals”或here 中描述)会生成一个唯一的随机数,而无需任何重试。它使用额外的存储空间来“标记”已经使用的数字,但如果随机生成的数字已经被占用(即没有重复的试错迭代),它仍然巧妙地设法立即提出一个未使用的数字。

该算法的一个问题是,即使它生成的数字通常不是有序的,但顺序仍然不是完全随机的。换句话说,即使该算法保证了它从范围中选择的每个单独的数字的均匀分布,它也不能保证结果序列在所有可能的结果序列中的均匀分布。

这对您来说是否重要 - 只有您可以决定。如果您生成的令牌数量与范围的长度相比较小,那么此算法将提供非常令人满意的结果。如果令牌的数量接近范围的长度,则该算法将生成“几乎排序”的序列。

【讨论】:

    【解决方案2】:

    每隔几个小时运行一次 cronjob,使用未使用的标识符重新填充 Redis 集合。让它在后台运行,这样当您需要时,只需将其从 Redis 集合中弹出即可。

    【讨论】:

      【解决方案3】:

      一种方法是混淆数字。我在我的文章Obfuscating Sequential Keys 中详细解释了这一点。代码示例是 C#,但应该不难翻译。

      【讨论】:

        【解决方案4】:
        cipher = OpenSSL::Cipher::AES.new(128, :CBC).encrypt
        cipher.update(MyModel.create.object_id.to_s(36))
        

        【讨论】:

          【解决方案5】:

          从序列中取出数字。实施 feistel 网络来打乱号码。 我在这里找到了一个例子:https://gist.github.com/Xeoncross/3715367

          【讨论】:

            【解决方案6】:

            您可以使用SecureRandom.uuid。我认为它保证了唯一的随机 UUID 令牌。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-01-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-11-21
              相关资源
              最近更新 更多