创建自己的Tinyurl风格的uid

我正在写一篇关于Guids / UID的人类可读替代品的小文章,例如在TinyURL上用于url哈希的那些(通常在杂志中打印,因此需要简短)。

我生成的简单uid是 – 6个字符:小写字母(az)或0-9。

“根据我的计算队长”,这是6个相互排斥的事件,虽然计算冲突的概率比P(A或B)= P(A)+ P(B)稍微硬一点,显然它包括数字和来自下面的代码,您可以看到它是否使用50/50的数字或字母。

我对冲突率很感兴趣,如果下面的代码是对生成哈希值的预期冲突率的真实模拟。 平均而言,我每百万得到40-50次冲突,但是考虑到uid不会一次产生一百万次,但可能每分钟只能产生10-1000次。

每次发生冲突的概率是多少,谁能建议更好的方式呢?

static Random _random = new Random(); public static void main() { // Size of the key, 6 HashSet set = new HashSet(); int clashes = 0; for (int n=0;n < 1000000;n++) { StringBuilder builder = new StringBuilder(); for (int i =0;i  0.5) { builder.Append((char)_random.Next(97,123)); } else { builder.Append(_random.Next(0,9).ToString()); } } if (set.Contains(builder.ToString())) { clashes++; Console.WriteLine("clash: (" +n+ ")" +builder.ToString()); } set.Add(builder.ToString()); _random.Next(); //Console.Write(builder.ToString()); } Console.WriteLine("Clashes: " +clashes); Console.ReadLine(); } 

更新: 这是这个问题的结果文章

我真的在这里问过两个问题,所以我在欺骗。 我追求的答案是rcar,但Sklivvz也是第二部分(另一种选择)的答案。 是否可以在数据库中创建自定义唯一ID生成器,或者它是客户端(首先是2个可能的读取)?

我之前的一般想法是在数据库或其他商店中使用ID,可以通过电话或印刷材料使用,而不是巨大的16字节guid。

更新2:我把两个相互排斥的事件的公式放在上面,而不是两个独立的事件(因为第一次得到’a’并不意味着你第二次不能得到’a’)。 应该是P(A和B)= P(A)x P(B)

与一个特定ID发生冲突的可能性是:

 p = ( 0.5 * ( (0.5*1/10) + (0.5*1/26) ) )^6 

约为1.7×10 ^ -9。

生成n个ID后发生冲突的概率是1-p ^ n,因此在插入100万个ID后,每次新插入的碰撞几率大约为0.17%,在1000万个ID后大约为1.7%,并且100万后约为16%。

每分钟1000个ID可达到大约4300万/月,正如Sklivvz指出的那样,在这种情况下使用一些递增ID可能是更好的方法。

编辑:

为了解释数学,他基本上是在掷硬币然后再挑选一个数字或字母6次。 硬币翻转匹配的概率为0.5,然后50%的时间有1/10的匹配机会和50%的概率匹配的概率为50%。 这种情况独立发生6次,因此您将这些概率相乘。

为什么要使用随机函数? 我总是假设tinyurl使用顺序Id的基础62(0-9A-Za-z)表示。 没有冲突,url总是尽可能短。

你会有一个DB表

 Id URL 1 http://google.com 2 ... ... ... 156 ... ... ... 

相应的URL将是:

 http://example.com/1 http://example.com/2 ... http://example.com/2W ... 

查看生日悖论 ,这是你遇到的确切问题。

问题是:你需要在一个房间里聚会多少人,这样你就有50%的机会让任何两个人拥有相同的生日? 答案可能会让你大吃一惊。

前段时间我做到了这一点,我按照Sklivvz提到的方式行事。 整个逻辑是使用SQL Server存储过程和几个UDF(用户定义的函数)开发的。 步骤是:

  • 说你想缩短这个url: 创建你自己的Tinyurl风格的uid
  • 将URL插入表中
  • 获取最后一次插入的@@ identity值(数字id)
  • 根据字母和数字的“域”转换相应字母数字值的id(我实际上使用了这个集合:“0123456789abcdefghijklmnopqrstuvwxyz”)
  • 返回那个值,比如’cc0′

转换是通过几个非常短的UDF实现的。

两个转换称为一个接一个将返回“顺序”值,如下所示:

 select dbo.FX_CONV (123456) -- returns "1f5n" select dbo.FX_CONV (123457) -- returns "1f5o" 

如果您有兴趣,我可以分享UDF的代码。

为什么不使用哈希算法呢? 并使用url的哈希?

如果你使用随机数,你可能会发生冲突,因为它们是不确定的。

哈希不可能是唯一的,但字符串的哈希值很有可能是唯一的。

更正

实际上等你想要它们是人类可读的…如果你把它们放在hex中它们在技术上是人类可读的。

或者您可以使用将哈希转换为人类可读字符串的算法。 如果人类可读的字符串是散列的不同表示,则它也应该作为散列“唯一”,即原始散列的基数36。

我将生成一个代表您要散列的数据的随机值,然后散列并检查clahses而不是尝试使用随机手动散列进行模拟。 这将为您提供更好的指标。 而且你会有更多随机性,因为你将有更多随机化(假设您的数据被散列更大:))。

如果您使用6个字符,az和0-9,那么总共36个字符。 因此排列的数量是36 ^ 6,即2176782336 ..所以它应该仅发生冲突1/2176782336次。

来自维基百科 :

当需要打印较少的字符时,GUID有时会编码为base64或Ascii85字符串。 Base64编码的GUID由22到24个字符组成(取决于填充),例如:

 7QDBkvCA1+B9K/U0vrQx1A 7QDBkvCA1+B9K/U0vrQx1A== 

和Ascii85编码只提供20个字符,例如:

 5:$Hj:Pf\4RLB9%kU\Lj 

因此,如果您关注唯一性,base64编码的GUID会让您更接近您想要的,尽管它不是6个字符。

最好先以字节为单位,然后将这些字节转换为hex显示,而不是直接使用字符。