我想证明一个GUID在一个简单的测试程序中不是唯一的。 我原以为下面的代码会运行几个小时,但它不起作用。我该怎么做呢?

BigInteger begin = new BigInteger((long)0);
BigInteger end = new BigInteger("340282366920938463463374607431768211456",10);  //2^128
for(begin; begin<end; begin++)
  Console.WriteLine(System.Guid.NewGuid().ToString());

我用的是c#。


当前回答

当然guid也会发生碰撞。由于guid是128位的,只需生成其中的2^128 + 1个,根据鸽子洞原理,肯定会有碰撞。

但是当我们说一个GUID是唯一的时,我们真正的意思是键空间非常大,实际上不可能意外地生成两次相同的GUID(假设我们是随机生成GUID)。

如果随机生成n个guid序列,那么至少发生一次碰撞的概率大约是p(n) = 1 - exp(-n^2 / 2 * 2^128)(这是一个生日问题,可能的生日数量为2^128)。

   n     p(n)
2^30 1.69e-21
2^40 1.77e-15
2^50 1.86e-10
2^60 1.95e-03

为了使这些数字具体化,2^60 = 1.15e+18。所以,如果你每秒生成10亿个guid,你将需要36年才能生成2^60个随机guid,即使这样,你发生碰撞的概率仍然是1.95e-03。在接下来的36年里,你更有可能在生命中的某个时刻被谋杀(4.76e-03),而不是发现一次碰撞。祝你好运。

其他回答

假设你有理由相信生成guid的算法并不是生成真正的随机数,而是以周期<< 2^128为周期循环。

例如,RFC4122方法用于派生guid,该guid固定某些位的值。

循环的证明取决于周期的可能大小。

对于小周期,哈希表(GUID) -> GUID与碰撞替换 如果guid不匹配(如果匹配则终止)可能是一种方法。也可以考虑只在随机的一小部分时间内进行替换。

最终,如果两次碰撞之间的最大周期足够大(并且事先不知道),任何方法都只能产生一个概率,即如果碰撞存在的话,就会发现碰撞。

请注意,如果生成guid的方法是基于时钟的(参见RFC),那么可能无法确定是否存在冲突,因为(a)您无法等待足够长的时间让时钟转一圈,或者(b)您无法在一个时钟滴答内请求足够的guid来强制碰撞。

或者,您可以显示Guid中位之间的统计关系,或者Guid之间位的相关性。这样的关系可能使得算法很有可能是有缺陷的,而不一定能找到实际的碰撞。

当然,如果您只是想证明Guids可以碰撞,那么答案就是数学证明,而不是程序。

[Update:] As the comments below point out, newer MS GUIDs are V4 and do not use the MAC address as part of the GUID generation (I haven't seen any indication of a V5 implementation from MS though, so if anyone has a link confirming that let me know). WIth V4 though, time is still a factor though, and the odds against duplication of GUIDs remains so small as to be irrelevant for any practical usage. You certainly would not be likely to ever generate a duplicate GUID from just a single system test such as the OP was trying to do.

大多数答案都忽略了微软GUID实现的一个关键点。GUID的第一部分基于时间戳,另一部分基于网卡的MAC地址(如果没有安装网卡,则为随机数)。

如果我理解正确,这意味着复制GUID的唯一可靠方法是在多台机器上同时运行GUID生成,其中MAC地址是相同的,并且两个系统上的时钟在生成发生时处于相同的确切时间(时间戳是基于毫秒的,如果我理解正确的话)....即使如此,数字中还有很多其他的位是随机的,所以几率仍然很小。

对于所有实际目的,guid都是惟一的。

在“旧的新事物”博客上有一个关于MS GUID的很好的描述

如果你担心独特性,你可以购买新的guid,这样你就可以扔掉旧的guid。如果你愿意,我可以把一些放在易趣网上。

你试过begin = begin+ new BigInteger((long)1)来代替begin++吗?

当然guid也会发生碰撞。由于guid是128位的,只需生成其中的2^128 + 1个,根据鸽子洞原理,肯定会有碰撞。

但是当我们说一个GUID是唯一的时,我们真正的意思是键空间非常大,实际上不可能意外地生成两次相同的GUID(假设我们是随机生成GUID)。

如果随机生成n个guid序列,那么至少发生一次碰撞的概率大约是p(n) = 1 - exp(-n^2 / 2 * 2^128)(这是一个生日问题,可能的生日数量为2^128)。

   n     p(n)
2^30 1.69e-21
2^40 1.77e-15
2^50 1.86e-10
2^60 1.95e-03

为了使这些数字具体化,2^60 = 1.15e+18。所以,如果你每秒生成10亿个guid,你将需要36年才能生成2^60个随机guid,即使这样,你发生碰撞的概率仍然是1.95e-03。在接下来的36年里,你更有可能在生命中的某个时刻被谋杀(4.76e-03),而不是发现一次碰撞。祝你好运。