【问题标题】:Generating a unique and random 6 character long string to represent link in ruby生成一个唯一且随机的 6 字符长字符串来表示 ruby​​ 中的链接
【发布时间】:2013-01-28 05:44:11
【问题描述】:

我正在生成一个唯一且随机的字母数字字符串段来表示将由用户生成的某些链接。为此,我使用 "uuid" 数字来确保它的唯一性和随机性,但是,根据我的要求,字符串的长度不应超过 5 个字符。所以我放弃了这个想法。

然后我决定使用 ruby​​ 的随机函数和当前时间戳生成这样一个字符串。 我的随机字符串的代码是这样的:-

 temp=DateTime.now
 temp=temp + rand(DateTime.now.to_i)
 temp= hash.abs.to_s(36)   

我所做的是将当前日期时间存储在一个临时变量中,然后生成一个随机数,将当前日期时间作为参数传递。然后在第二行中实际将当前日期时间和随机数加在一起,形成一个唯一且随机的字符串。

很快我发现,当我在两台不同的机器上测试我的应用程序并同时发送请求时,经过 100 多次试验,它生成了一次相同的字符串(虽然很少见)。

现在我想在将 to_s(36) 传递到 temp 变量之前,我应该再添加一个参数,例如 mac 地址或客户端 IP 地址。但无法弄清楚如何做到这一点,即使那样它是否会是独一无二的......

谢谢....

【问题讨论】:

  • 5 个字符(40 位)是相当少量的数据。对于小型顺序计数器来说没问题,但对于“随机唯一”数字 - 不是那么多。
  • 在rails中,你可以通过调用request.remote_ip获取客户端ip。如何在散列逻辑中编织它取决于您。
  • @SergioTulentsev 我必须让一个字符串看起来有点像...bcoz它将代表一个链接,根据请求将重定向到一个或多个长网址...
  • 位链接不是随机的,AFAIK。它们是简单的 base-62 数字。
  • 这就是 5 个字符的问题。使用更多字符。

标签: ruby-on-rails ruby


【解决方案1】:

SecureRandom 在 ruby​​ 中使用进程 ID(如果可用)和当前时间。您可以使用 urlsafe_base64(n= 16) 类方法来生成您需要的序列。根据您的要求,我认为这是您最好的选择。

编辑:经过一番测试,我仍然认为这种方法会生成非唯一键。我为条形码生成解决这个问题的方法是:

barcode= barcode_sql_id_hash("#{sql_id}#{keyword}")

这里,您的关键字可以是时间 + pid。

【讨论】:

  • 或者只是SecureRandom.hex(5) :)
  • @nurettin 我正在使用“SecureRandom.urlsafe_base64(4, true)”..这看起来不错,但唯一的问题是很多时候它在字符串中插入“-”字符...可以我生成它时没有“-”??
  • @Siddharth '-' 是 URL-Safe base64 字符集的一部分。我建议它是因为您想在链接中使用它,所以没关系。由于 SecureRandom 使用 OpenSSL 随机加密函数,它会生成尝试在每台计算机上唯一的 id。但是,您可能仍然会遇到哈希冲突,因此我建议您更改方法,使用“所涉及记录的 id”来播种哈希。
【解决方案2】:

如果您确定您永远不需要超过给定 M 数量的唯一值,并且您只需要防止猜测下一个生成的 id 的基本保护,您可以使用Linear Congruentual Generator 来生成您的标识符.您所要做的就是记住最后生成的 id,并使用以下公式使用它生成一个新的:

newid = (A * oldid + B) mod M

如果 2³² 不同的 id 值足以满足您的需求,请尝试:

def generate_id
  if @lcg
    @lcg = (1664525 * @lcg + 1013904223) % (2**32)
  else
    @lcg = rand(2**32) # Random seed
  end
end

现在只需选择一组合适的字符来表示 id,最少 6 个字符。大写和小写字母都可以解决问题,因为 (26+26)^6 > 2^32:

ENCODE_CHARS = [*?a..?z, *?A..?Z]

def encode(n)
  6.times.map { |i|
    n, mod = n.divmod(ENCODE_CHARS.size)
    ENCODE_CHARS[mod]
  }.join
end

例子:

> 10.times { n = generate_id ; puts "%10d = %s" % [n, encode(n)] }

2574974483 = dyhjOg
3636751446 = QxyuDj
 368621501 = bBGvYa
1689949688 = yuTgxe
1457610999 = NqzsRd
3936504298 = MPpusk
 133820481 = PQLpsa
2956135596 = yvXpOh
3269402651 = VFUhFi
 724653758 = knLfVb

由于 LCG 的性质,在所有 2³² 值每个都被使用一次之前,生成的 id 不会重复。

【讨论】:

【解决方案3】:

您无法生成只有五个字符的唯一 UUID,对于字符和数字,您的基本空间约为 56 个字符,因此最多有 56^5 个组合,大约 5.51 亿(大约 2^29)。

如果使用此方案,您将要生成 10.000 个 UUID(UUID 的数量非常少),您将有 1/5.000 的概率生成冲突。 使用加密时,足够大空间以避免冲突的标准定义是 2^80 左右。

从这个角度来看,如果你的算法只生成一个随机整数(32 位 uint 是 2^32,是你提议的大小的 8 倍),你的算法会更好,这显然是个坏主意。

【讨论】:

  • 唯一性问题可以通过对数据库进行测试或尝试插入并在插入失败时使用新的令牌生成进行救援来轻松解决。确实,空间很小,但碰撞的代价也是如此,直到 ha 开始填满整个空间......
猜你喜欢
  • 1970-01-01
  • 2013-03-05
  • 2013-05-20
  • 2012-11-21
  • 2010-10-18
  • 2011-02-17
  • 1970-01-01
  • 2011-08-18
相关资源
最近更新 更多