简单证明GUID不是唯一的

时间:2009-11-10 00:55:24

标签: c# guid

我想证明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#。

30 个答案:

答案 0 :(得分:407)

Kai,我提供了一个程序,可以使用线程执行您想要的操作。它根据以下条款获得许可:您必须为每个CPU核心每小时支付0.0001美元。费用在每个日历月结束时支付。请尽快与我联系,以获取我的paypal帐户详细信息。

using System;
using System.Collections.Generic;
using System.Linq;

namespace GuidCollisionDetector
{
    class Program
    {
        static void Main(string[] args)
        {
            //var reserveSomeRam = new byte[1024 * 1024 * 100];     // This indeed has no effect.

            Console.WriteLine("{0:u} - Building a bigHeapOGuids.", DateTime.Now);
            // Fill up memory with guids.
            var bigHeapOGuids = new HashSet<Guid>();
            try
            {
                do
                {
                    bigHeapOGuids.Add(Guid.NewGuid());
                } while (true);
            }
            catch (OutOfMemoryException)
            {
                // Release the ram we allocated up front.
                // Actually, these are pointless too.
                //GC.KeepAlive(reserveSomeRam);
                //GC.Collect();
            }
            Console.WriteLine("{0:u} - Built bigHeapOGuids, contains {1} of them.", DateTime.Now, bigHeapOGuids.LongCount());


            // Spool up some threads to keep checking if there's a match.
            // Keep running until the heat death of the universe.
            for (long k = 0; k < Int64.MaxValue; k++)
            {
                for (long j = 0; j < Int64.MaxValue; j++)
                {
                    Console.WriteLine("{0:u} - Looking for collisions with {1} thread(s)....", DateTime.Now, Environment.ProcessorCount);
                    System.Threading.Tasks.Parallel.For(0, Int32.MaxValue, (i) =>
                    {
                        if (bigHeapOGuids.Contains(Guid.NewGuid()))
                            throw new ApplicationException("Guids collided! Oh my gosh!");
                    }
                    );
                    Console.WriteLine("{0:u} - That was another {1} attempts without a collision.", DateTime.Now, ((long)Int32.MaxValue) * Environment.ProcessorCount);
                }
            }
            Console.WriteLine("Umm... why hasn't the universe ended yet?");
        }
    }
}

PS:我想试试Parallel扩展库。这很容易。

使用OutOfMemoryException作为控制流只是感觉不对。

修改

嗯,似乎这仍然吸引了选票。所以我修复了GC.KeepAlive()问题。并将其更改为使用C#4运行。

澄清我的支持条款:支持仅适用于2010年2月28日。请使用时间机器仅在当天提出支持请求。

编辑2 与往常一样,GC比管理内存做得更好;以前任何以前做过的尝试都注定要失败。

答案 1 :(得分:226)

这将持续超过数小时。假设它以1 GHz的频率循环(它不会 - 它将慢很多),它将运行10790283070806014188970年。这比宇宙时代长约830亿倍。

假设Moores law成立,那么不运行此程序,等待数百年并在速度快数十亿的计算机上运行它会快得多。实际上,如果等到CPU速度增加并且在运行之前购买新的CPU,那么任何需要运行时间超过CPU速度加倍(大约18个月)的程序都会很快完成(除非你写它以便它可以在新硬件上暂停和恢复。)

答案 2 :(得分:170)

GUID理论上是非唯一的。这是你的证据:

  • GUID是128位数字
  • 如果不重新使用旧GUID,则无法生成2 ^ 128 + 1个或更多GUID

然而,如果太阳的整个输出功率都是针对执行此任务的,那么在它完成之前很久就会变冷。

GUID可以使用多种不同的策略生成,其中一些策略采取特殊措施来保证给定的机器不会两次生成相同的GUID。在特定算法中查找冲突会显示您生成GUID的特定方法很糟糕,但一般不会证明GUID的任何内容。

答案 3 :(得分:137)

当然GUID可能会发生冲突。由于GUID是128位,因此只生成它们2^128 + 1,而pigeonhole principle必须发生冲突。

但是当我们说GUID是唯一的时,我们真正的意思是密钥空间太大以至于几乎不可能意外地生成相同的GUID两次(假设我们随机生成GUID)。

如果您随机生成一系列n GUID,则至少一次碰撞的概率大约为p(n) = 1 - exp(-n^2 / 2 * 2^128)(这是birthday problem,可能的生日数为{{ 1}})。

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 。因此,如果您每秒生成10亿个GUID,则生成2^60 = 1.15e+18个随机GUID需要36年时间,即使这样,您发生碰撞的概率仍为2^60。在接下来的36年中,你更有可能成为murdered at some point in your life1.95e-03)。祝你好运。

答案 4 :(得分:61)

如果您担心独特性,您可以随时购买新的GUID,这样您就可以扔掉旧的GUID。如果你愿意,我会在eBay上放一些。

答案 5 :(得分:47)

就个人而言,我认为“大爆炸”是在两个GUID相撞时引起的。

答案 6 :(得分:42)

您可以在O(1)时间内使用quantum bogosort算法的变体显示。

Guid g1 = Guid.NewGuid();
Guid g2 = Guid.NewGuid();
if(g1 != g2) Universe.Current.Destroy();

答案 7 :(得分:28)

任何两个GUID都很可能是唯一的(不相等)。

请参阅this SO entryWikipedia

  

虽然每个生成的GUID都不是   保证是独一无二的,总数   唯一键的数量(2 ^ 128或   3.4×10 ^ 38)是如此之大,以至于相同数量的概率   生成两次非常小。对于   例如,考虑可观察性   宇宙,其中包含约5×10 ^ 22   星星;那么每个明星都可以拥有   6.8×10 ^ 15个通用唯一的GUID。

所以你可能需要等待数十亿年,并且希望你在宇宙之前击中一个,因为我们知道它已经结束了。

答案 8 :(得分:27)

[更新:] 正如下面的评论所指出的,较新的MS GUID是V4,并且不使用MAC地址作为GUID生成的一部分(我没有看到任何迹象虽然有来自MS的V5实现,所以如果有人有链接确认让我知道)。但是,对于V4来说,时间仍然是一个因素,并且GUID重复的可能性仍然很小,与任何实际用法无关。您当然不可能仅通过OP尝试执行的单个系统测试生成重复的GUID。

这些答案中的大多数都缺少关于微软GUID实施的一个重要观点。 GUID的第一部分基于时间戳,另一部分基于网卡的MAC地址(如果未安装NIC,则为随机数)。

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

出于所有实际目的,GUID具有普遍的独特性。

"The Old New Thing" blog

处有一个很好的MS GUID描述

答案 9 :(得分:23)

如果你想在你的代码中的许多地方检查guid唯一性,可以使用这个漂亮的小扩展方法。

internal static class GuidExt
{
    public static bool IsUnique(this Guid guid)
    {
        while (guid != Guid.NewGuid())
        { }
        return false;
    }
}

要调用它,只需在生成新guid时调用Guid.IsUnique ...

Guid g = Guid.NewGuid();
if (!g.IsUnique())
{
    throw new GuidIsNotUniqueException();
}

......哎呀,我甚至建议两次打电话来确保它在第一轮中正确。

答案 10 :(得分:19)

数到2 ^ 128 - 雄心勃勃。

让我们想象一下,每台机器每秒可以计算2 ^ 32个ID - 而 雄心勃勃,因为它甚至不是每秒43亿。让2 ^ 32台机器专门用于该任务。此外,让每个文明都有2 ^ 32个文明将相同的资源用于任务。

到目前为止,我们每秒可以计算2 ^ 96个ID,这意味着我们将计算2 ^ 32秒(略超过136年)。

现在,我们所需要的只是为每台专用的4,294,967,296台机器获得4,294,967,296个文明,每台机器每秒能够计算4,294,967,296个ID,在接下来的136年左右完全是为了这个任务 - 我建议我们开始这个重要的任务现在; - )

答案 11 :(得分:17)

如果830亿年的运行时间没有吓到你,那么你认为你还需要将生成的GUID存储在某处以检查你是否有重复;存储2 ^ 128个16字节的数字只需要你预先分配4951760157141521099596496896TB的RAM,所以想象你有一台计算机可以适应所有这些并且你以某种方式找到一个地方购买每个10克的太字节DIMM,它们将结合起来重量超过8个地球质量,因此在你按下“运行”之前,你可以认真地将它从当前的轨道上移开。三思而后行!

答案 12 :(得分:12)

for(begin; begin<end; begin)
    Console.WriteLine(System.Guid.NewGuid().ToString());

您没有递增begin,因此条件begin < end始终为真。

答案 13 :(得分:11)

如果担心GUID冲突,我建议改为使用ScottGuID

答案 14 :(得分:9)

但是你必须确定你有重复,或者你只关心可以是否重复。为了确保你有两个同一个生日的人,你需要366人(不计算闰年)。因为有两个人生日相同的可能性超过50%,你只需要23个人。这是birthday problem

如果您有32位,则只需要77,163个值就有超过50%的重复几率。试试吧:

Random baseRandom = new Random(0);

int DuplicateIntegerTest(int interations)
{
    Random r = new Random(baseRandom.Next());
    int[] ints = new int[interations];
    for (int i = 0; i < ints.Length; i++)
    {
        ints[i] = r.Next();
    }
    Array.Sort(ints);
    for (int i = 1; i < ints.Length; i++)
    {
        if (ints[i] == ints[i - 1])
            return 1;
    }
    return 0;
}

void DoTest()
{
    baseRandom = new Random(0);
    int count = 0;
    int duplicates = 0;
    for (int i = 0; i < 1000; i++)
    {
        count++;
        duplicates += DuplicateIntegerTest(77163);
    }
    Console.WriteLine("{0} iterations had {1} with duplicates", count, duplicates);
}

1000 iterations had 737 with duplicates

现在128位是很多,所以你仍然在谈论大量的物品仍然让你碰撞的几率很低。使用近似值,您将需要以下给定赔率的记录数:

  • 发生碰撞的可能性为1/1000亿美元
  • 217亿亿,发生碰撞的几率为50%
  • 396亿美元,发生碰撞的几率为90%

每年发送大约1E14封电子邮件,所以在此级别上大约有400,000年的电子邮件,然后才有90%的机会让两个人拥有相同的GUID,但这与说你需要运行一个很大的不同。计算机是宇宙年龄的830亿倍,或者在找到副本之前太阳会变冷。

答案 15 :(得分:9)

据推测,你有理由相信用于生成Guids的算法不会生成真正的随机数,但实际上是以周期&lt;&lt; 2 ^ 128。

e.g。 RFC4122用于派生GUID的方法,用于修复某些位的值。

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

对于小时段,散列哈希表(GUID) - &gt; GUID在碰撞时更换 如果GUID不匹配(如果它们确实终止)可能是一种方法。考虑也只是替换随机时间的一小部分。

最终,如果碰撞之间的最大周期足够大(并且事先不知道),任何方法只会产生碰撞的概率(如果存在的话)。

请注意,如果生成Guids的方法是基于时钟的(参见RFC),则可能无法确定是否存在冲突,因为(a)您将无法等待足够长的时钟环绕,或(b)你不能在时钟滴答内请求足够的Guid来强迫碰撞。

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

当然,如果你只是想证明Guids可以碰撞,那么数学证明就是答案。

答案 16 :(得分:8)

我不明白为什么没有人提到升级你的显卡...当然如果你有一个高端的NVIDIA Quadro FX 4800或其他东西(192个CUDA核心),这会更快......

当然,如果您能买得起一些NVIDIA Qadro Plex 2200 S4(每个CUDA核心960个),这个计算 真的 尖叫。也许NVIDIA会愿意借给你一些“技术示范”作为公关噱头?

当然,他们希望成为历史计算的一部分......

答案 17 :(得分:7)

  1. 前往纽约市的低温实验室。
  2. 冻结自己(大致)1990年。
  3. 在Planet Express找到一份工作。
  4. 购买全新的CPU。构建一台计算机,运行该程序,并使用伪万向运动机器(如世界末日机器)将其放置在安全的地方。
  5. 等到发明时间机器。
  6. 使用时间机器跳转到未来。如果您购买了1YHz 128位CPU,请在开始运行程序后转到3,938,453,320 days 20 hours 15 minutes 38 seconds 463 ms 463 μs 374 ns 607 ps
  7. ...?
  8. PROFIT !!!
  9. ......即使您有1YHz的CPU,10,783,127(或1,000,000,000,000,000,如果您更喜欢使用二进制前缀)比1GHz CPU快,也需要至少1,125,899,906,842,624年。

    因此,不是等待计算完成,最好是喂养失去家园的鸽子,因为其他n鸽子带回家。 :(

    或者,您可以等到发明128位量子计算机。然后,您可以通过在合理的时间(可能)使用您的程序来证明GUID不是唯一的。

答案 18 :(得分:7)

您可以散列GUID。这样,你应该更快地得到一个结果。

哦,当然,同时运行多个线程也是一个好主意,这样你就可以增加竞争条件在不同线程上生成相同GUID两次的机会。

答案 19 :(得分:7)

你们不是都错过了重点吗?

我认为GUID是使用两个东西生成的,这使得它们在全球范围内的独特性非常高。一个是他们使用您所在机器的MAC地址播种,两个使用它们生成的时间加上一个随机数。

因此,除非您在实际机器上运行并在机器用于表示GUID中的时间的最短时间内运行所有猜测,否则无论您使用多少猜测,您都不会生成相同的数字。系统调用。

我想如果你知道制作GUID的实际方式,实际上会缩短猜测的时间。

答案 20 :(得分:6)

GUID是124位,因为4位保存版本号。

答案 21 :(得分:4)

您是否尝试begin = begin + new BigInteger((long)1)代替开始++?

答案 22 :(得分:4)

如果生成的UUID数量遵循摩尔定律,那么在可预见的未来永远没有用完GUID的印象是错误的。

使用2 ^ 128个UUID,在我们用完所有UUID之前,它只需要18个月* Log2(2 ^ 128)〜= 192年。

而且我相信(自从大规模采用UUID以来的过去几年没有统计证据),我们生成UUID的速度正在以比摩尔定律更快的速度增长。换句话说,我们可能还有不到192年的时间,直到我们必须处理UUID危机,这比宇宙结束要快得多。

但是,由于我们肯定不会在2012年底之前完成它们,我们会把它留给其他物种担心这个问题。

答案 23 :(得分:3)

GUID生成代码中的错误的几率远高于生成碰撞的算法的几率。您的代码中测试GUID的错误的可能性更大。放弃。

答案 24 :(得分:3)

这里没有篝火,但它确实发生了,是的,我理解你一直在给这个家伙开玩笑,但GUID原则上是唯一的,我碰到了这个帖子,因为那里是WP7模拟器中的一个错误,这意味着每次启动时它都会在第一次调用时发出相同的指令!所以,理论上你不能有冲突,如果生成所述GUI有问题,那么你可以得到重复

http://forums.create.msdn.com/forums/p/92086/597310.aspx#597310

答案 25 :(得分:2)

该程序虽然有错误,但却证明GUID不是唯一的。那些试图证明相反的人忽略了这一点。这个陈述只是证明了一些GUID变体的弱实现。

根据定义,GUID不是唯一的,根据定义,它是非常独特的。你刚刚完善了高度的含义。取决于版本,实现者(MS或其他),VM的使用等您对高度更改的定义。 (见前文中的链接)

您可以缩短128位表以证明您的观点。最好的解决方案是使用哈希公式来缩短具有重复项的表,然后在哈希冲突后使用完整值并基于该值重新生成GUID。如果从不同位置运行,则会将哈希/完整密钥对存储在中心位置。

Ps:如果目标只是生成x个不同的值,请创建此宽度的哈希表,并检查哈希值。

答案 26 :(得分:1)

由于Guid一代的部分是基于当前机器的时间,我的理论是获得一个重复的Guid:

  1. 执行Windows的全新安装
  2. 创建一个启动脚本,在Windows启动时将时间重置为2010-01-01 12:00:00。
  3. 在启动脚本之后,它会触发您的应用程序生成Guid。
  4. 克隆此Windows安装,以便排除后续启动过程中可能出现的任何细微差别。
  5. 使用此图像重新映像硬盘驱动器并启动机器几次。

答案 27 :(得分:0)

这也是一个解决方案:

int main()
{
  QUuid uuid;
  while ( (uuid = QUuid::createUuid()) != QUuid::createUuid() ) { }
  std::cout << "Aha! I've found one! " << qPrintable( uuid.toString() ) << std::endl;
}

注意:需要Qt,但我保证如果你让它运行得足够长,它可能会找到一个。

(注意:实际上,现在我正在看它,可能有一些关于生成算法的东西阻止了两个随后产生的碰撞的uuids - 但我有点怀疑它。)

答案 28 :(得分:0)

对我而言......单个核心生成UUIDv1所需的时间保证它是唯一的。即使在多核情况下,如果UUID生成器只允许一次为您的特定资源生成一个UUID(请记住,多个资源可以完全利用相同的UUID,但不太可能,因为资源本身就是地址的一部分),那么您将有足够的UUID来持续你,直到时间戳烧毁。在这一点上,我真的怀疑你会关心。

答案 29 :(得分:0)

证明GUID不唯一的唯一解决方案是拥有一个World GUID Pool。每次在某处生成GUID时,都应该向组织注册。或者,我们可能会包含一个标准化,所有GUID生成器都需要自动注册它,因此需要有效的互联网连接!