我正在写一篇关于Guids / UID的人类可读替代品的小文章,例如在TinyURL上用于url哈希的那些(通常在杂志中打印,因此需要简短)。
我生成的简单uid是 - 6个字符:小写字母(a-z)或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<string> set = new HashSet<string>();
int clashes = 0;
for (int n=0;n < 1000000;n++)
{
StringBuilder builder = new StringBuilder();
for (int i =0;i < 7;i++)
{
if (_random.NextDouble() > 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();
}
更新 Here's the resulting article来自这个问题
我真的在这里问过两个问题,所以我在欺骗。我追求的答案是rcar,但Sklivvz也是第二部分(另一种选择)的答案。是否可以在数据库中创建自定义唯一ID生成器,或者它是客户端(首先是2个可能的读取)?
我所追求的一般想法是在数据库或其他可以通过电话或印刷材料使用的商店中使用ID,而不是一个巨大的16字节guid。
更新2:我把两个互斥事件的公式放在上面,而不是两个独立事件(因为第一次获得'a'并不意味着你不能得到'a' '第二次)。应该是P(A和B)= P(A)x P(B)
答案 0 :(得分:31)
为什么要使用随机函数?我总是假设tinyurl使用顺序Id的基础62(0-9A-Za-z)表示。没有冲突,网址总是尽可能短。
你会有一个像
这样的数据库表Id URL
1 http://google.com
2 ...
... ...
156 ...
... ...
,相应的网址为:
http://example.com/1
http://example.com/2
...
http://example.com/2W
...
答案 1 :(得分:6)
查看Birthday Paradox,这是您遇到的确切问题。
问题是:你需要在一个房间里聚会多少人,这样你就有50%的机会让任何两个人有相同的生日?答案可能让你大吃一惊。
答案 2 :(得分:5)
前段时间我做到了这一点,我按照Sklivvz提到的方式行事。整个逻辑是使用SQL服务器存储过程和几个UDF(用户定义的函数)开发的。步骤是:
通过几个非常短的UDF实现了转换。
一个接一个地调用的两个转换将返回“顺序”值,如下所示:
select dbo.FX_CONV (123456) -- returns "1f5n"
select dbo.FX_CONV (123457) -- returns "1f5o"
如果您有兴趣,我可以分享UDF的代码。
答案 3 :(得分:4)
与一个特定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万之后大约为1.7% ID,在1亿之后约为16%。
每分钟1000个ID可达到4300万/月,正如Sklivvz指出的那样,在这种情况下,使用一些递增ID可能是更好的方法。
编辑:
为了解释数学,他基本上是在掷硬币,然后挑选一个数字或字母6次。硬币翻转匹配的概率为0.5,然后50%的时间有1/10的匹配机会和50%的概率匹配的概率为50%。这种情况独立发生6次,因此您将这些概率相乘。
答案 4 :(得分:0)
为什么不使用散列算法呢?并使用网址的哈希值?
如果你使用随机数,你可能会因冲突不确定而发生冲突。
哈希不可能是唯一的,但字符串的哈希很可能是唯一的。
<强>校正强>
实际上等你想让它们具有人性化的可读性......如果你把它们放在十六进制中它们在技术上是人类可读的。
或者您可以使用将哈希转换为人类可读字符串的算法。如果人类可读的字符串是散列的不同表示,那么它也应该像散列一样“唯一”,即原始散列的基数36。
答案 5 :(得分:0)
我会生成一个代表您要散列的数据的随机值,然后散列并检查clahses,而不是尝试使用随机手动散列进行模拟。这将为您提供更好的指标。而且你会有更多随机性,因为你将有更多随机化(假设您的数据被散列更大:))。
答案 6 :(得分:0)
如果您使用6个字符,a-z和0-9,则总共36个字符。排列的数量因此是36 ^ 6,即2176782336 ..所以它应该只发生冲突1/2176782336次。
答案 7 :(得分:0)
来自wikipedia:
当需要打印较少的字符时,GUID有时会编码为base64或Ascii85字符串。 Base64编码的GUID由22到24个字符组成(取决于填充),例如:
7QDBkvCA1+B9K/U0vrQx1A
7QDBkvCA1+B9K/U0vrQx1A==
和Ascii85编码只提供20个字符,例如。 G:
5:$Hj:Pf\4RLB9%kU\Lj
因此,如果你关注唯一性,base64编码的GUID会让你更接近你想要的东西,尽管它不是6个字符。
最好先以字节为单位,然后将这些字节转换为十六进制显示,而不是直接使用字符。