生成一个唯一但显然是随机的标识符,无需循环+重试

时间:2013-03-22 20:38:17

标签: ruby-on-rails ruby algorithm

我正在尝试使用算法为我的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,但这是一种适用于任何语言的通用算法问题,因此欢迎使用其他语言的解决方案。

6 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分:2)

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

答案 2 :(得分:2)

每隔几个小时运行一次cronjob,重新填充带有未使用标识符的Redis集合。让它在后台运行,所以当你需要它时,只需将它从Redis集合中弹出。

答案 3 :(得分:2)

cipher = OpenSSL::Cipher::AES.new(128, :CBC).encrypt
cipher.update(MyModel.create.object_id.to_s(36))

答案 4 :(得分:1)

从序列中取出数字。实施一个feistel网络来加扰数字。 我找到的一个例子是:https://gist.github.com/Xeoncross/3715367

答案 5 :(得分:1)

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