web-dev-qa-db-fra.com

Preuve simple que GUID n'est pas unique

Je voudrais prouver qu'un GUID n'est pas unique dans un programme de test simple. Je m'attendais à ce que le code suivant s'exécute pendant des heures, mais cela ne fonctionne pas. Comment puis-je le faire fonctionner?

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());

J'utilise C #.

323
Kai

Kai, j'ai fourni un programme qui fera ce que vous voulez en utilisant des threads. Il est sous licence selon les termes suivants: vous devez me payer 0,0001 USD par heure et par cœur de processeur sur lequel vous l’exécutez. Les frais sont payables à la fin de chaque mois civil. S'il vous plaît contactez-moi pour les détails de mon compte Paypal dès que possible.

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: Je voulais essayer la bibliothèque d'extensions parallèles. C'était facile.

Et l’utilisation de OutOfMemoryException en tant que flux de contrôle n’est pas bonne.

EDIT

Eh bien, il semble que cela attire toujours les votes. J'ai donc résolu le problème GC.KeepAlive (). Et changé pour fonctionner avec C # 4.

Et pour clarifier mes conditions de support: le support n’est disponible que le 28/02/2010. Veuillez utiliser une machine à voyager dans le temps pour faire des demandes d'assistance ce jour-là uniquement.

EDIT 2 Comme toujours, le GC fait un meilleur travail que moi en matière de gestion de la mémoire; toute tentative antérieure de le faire moi-même était vouée à l'échec.

407
ligos

Cela durera beaucoup plus que des heures. En supposant que la boucle se situe à 1 GHz (ce qui ne sera pas le cas - ce sera beaucoup plus lent que cela), elle fonctionnera pendant 1079028307070606014188970 ans. Ce qui est environ 83 milliards de fois plus long que l'âge de l'univers.

En supposant que loi de Moores , il serait beaucoup plus rapide de ne pas exécuter ce programme, d'attendre plusieurs centaines d'années et de l'exécuter sur un ordinateur qui est des milliards de fois plus rapide. En fait, tout programme dont l’exécution prend plus de temps que de doubler (environ 18 mois) s’achève plus tôt si vous attendez que la vitesse du processeur augmente et que vous en achetiez un nouveau avant de le lancer (à moins que vous ne l’écriviez peut être suspendu et repris sur du nouveau matériel).

226
rjmunro

Un GUID est théoriquement non unique. Voici votre preuve:

  • Le GUID est un nombre de 128 bits
  • Vous ne pouvez pas générer 2 ^ 128 + 1 ou plusieurs GUID sans réutiliser d'anciens GUID

Cependant, si toute la puissance du Soleil était destinée à cette tâche, elle serait froide bien avant son achèvement.

Les GUID peuvent être générés à l'aide d'un certain nombre de tactiques différentes, dont certaines prennent des mesures spéciales pour garantir qu'une machine donnée ne génère pas le même GUID. La recherche de collisions dans un algorithme particulier montrerait que votre méthode particulière de génération de GUID est incorrecte, mais ne prouverait rien des GUID en général.

170
tylerl

Bien sûr, les GUID peuvent entrer en collision. Les GUID étant de 128 bits, il suffit de générer 2^128 + 1 d'eux et par le principe du casier il doit y avoir une collision.

Mais quand nous disons qu'un GUID est unique, nous voulons dire que l'espace clé est si grand qu'il est pratiquement impossible de générer accidentellement le même GUID deux fois (en supposant que nous générons des GUID de manière aléatoire).

Si vous générez une séquence de n GUID de manière aléatoire, la probabilité d'au moins une collision est d'environ p(n) = 1 - exp(-n^2 / 2 * 2^128) (il s'agit du problème d'anniversaire , le nombre de anniversaires possibles étant 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

Pour concrétiser ces chiffres, 2^60 = 1.15e+18. Ainsi, si vous générez un milliard de GUID par seconde, il vous faudra 36 ans pour générer 2^60 GUID aléatoires, et même dans ce cas, la probabilité d'une collision est toujours de 1.95e-03. Vous êtes plus susceptible d'être assassiné à un moment de votre vie (4.76e-03) que de trouver une collision au cours des 36 prochaines années. Bonne chance.

137
jason

Si vous êtes inquiet à propos de l'unicité, vous pouvez toujours acheter de nouveaux GUID afin de pouvoir jeter vos anciens. Je vais en mettre sur eBay si vous le souhaitez.

61
ctacke

Personnellement, je pense que le "Big Bang" a été provoqué par la collision de deux GUID.

47
AMissico

Vous pouvez montrer que dans O(1) time avec une variante de l'algorithme quantum bogosort .

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

Deux GUID sont très probablement uniques (non égaux).

Voir this SO entry , et de Wikipedia

Bien que chaque GUID généré ne soit pas unique, le nombre total de clés uniques (2 ^ 128 ou 3,4 × 10 ^ 38) est si grand que la probabilité qu'un même nombre soit généré deux fois est très faible. . Par exemple, considérons l'univers observable, qui contient environ 5 × 10 ^ 22 étoiles; chaque étoile pourrait alors avoir 6,8 × 10 ^ 15 GUID universellement uniques.

Vous devez donc probablement attendre encore plusieurs milliards d’années et espérer en atteindre un avant l’univers tel que nous le connaissons, car il se termine.

28
Graviton

[Update:] Comme le soulignent les commentaires ci-dessous, les nouveaux GUID MS sont la V4 et n'utilisent pas l'adresse MAC dans le cadre de la génération GUID N'a pas vu d'indication d'une implémentation de la V5 de MS, donc si quelqu'un a un lien confirmant cela, faites le moi savoir). Cependant, avec la V4, le temps est toujours un facteur, et les chances de duplication de GUID restent si minimes qu’elles n’ont aucune pertinence pour un usage pratique. Vous ne serez certainement pas susceptible de générer un doublon GUID à partir d'un seul test de système tel que celui que l'OP essayait de faire.

Il manque dans la plupart de ces réponses un élément essentiel de la mise en œuvre de Microsoft GUID. La première partie du GUID est basée sur un horodatage et une autre partie est basée sur l'adresse MAC de la carte réseau (ou un nombre aléatoire si aucun NIC n'est installé).

Si je comprends bien, cela signifie que le seul moyen fiable de dupliquer un GUID serait d'exécuter simultanément des générations GUID sur plusieurs machines où les adresses MAC étaient les mêmes ET où les horloges des deux systèmes étaient au même moment exact où la génération a eu lieu (l'horodatage est basé sur des millisecondes si je le comprends bien) .... même dans ce cas, il y a beaucoup d'autres bits dans le nombre qui sont aléatoires, de sorte que les chances sont encore extrêmement faibles.

À toutes fins pratiques, les GUID sont universellement uniques.

Il y a une assez bonne description du MS GUID sur blog "The Old New Thing"

27
Stephen M. Redd

Voici une petite méthode d’extension astucieuse que vous pouvez utiliser si vous souhaitez vérifier l’unicité de GUID dans de nombreux endroits de votre code.

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

Pour l'appeler, appelez simplement Guid.IsUnique chaque fois que vous générez un nouveau guide ...

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

... diable, je recommanderais même de l'appeler deux fois pour s'assurer que tout se passe bien dès le premier tour.

23
KristoferA

Compter à 2 ^ 128 - ambitieux.

Imaginons que nous puissions compter 2 ^ 32 ID par seconde par machine - pas cela ambitieux, puisque ce n'est même pas 4,3 milliards par seconde. Permet de dédier 2 ^ 32 machines à cette tâche. De plus, il faut que 2 ^ 32 civilisations consacrent chacune les mêmes ressources à la tâche.

Jusqu'ici, nous pouvons compter 2 ^ 96 ID par seconde, ce qui signifie que nous compterons pour 2 ^ 32 secondes (un peu plus de 136 ans).

Maintenant, tout ce dont nous avons besoin, c'est d'obtenir que 4 294 967 296 civilisations consacrent chacune 4 294 967 296 machines, chaque machine pouvant compter 4 294 967 296 ID par seconde, uniquement pour cette tâche pour les 136 prochaines années environ. Je suggère que nous commencions immédiatement à cette tâche essentielle; -)

19
Steve314

Eh bien, si la durée de fonctionnement de 83 milliards d’années ne vous effraie pas, pensez que vous aurez également besoin de stocker les GUID générés quelque part pour vérifier si vous avez un doublon; pour stocker 2 ^ 128 numéros sur 16 octets, il vous suffirait d'allouer 4951760157141521099596496896 téraoctets de RAM à l'avance. Chaque gramme combiné, ils pèsent plus de 8 masses terrestres, vous pouvez donc sérieusement le déplacer hors de l'orbite actuelle, avant même d'appuyer sur "Run". Pensez-y à deux fois!

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

Vous n'incrémentez pas begin donc la condition begin < end est toujours vraie.

12
Nathan Taylor

Si les collisions GUID posent un problème, je vous recommande d'utiliser plutôt ScottGuID .

11
Matt Peterson

Mais devez-vous être sûr que vous ayez un duplicata, ou ne vous souciez-vous que si pouvez être un doublon. Pour être sûr que deux personnes ont le même anniversaire, vous avez besoin de 366 personnes (sans compter les années bissextiles). Pour qu'il y ait plus de 50% de chances d'avoir deux personnes avec le même anniversaire, vous n'avez besoin que de 23 personnes. C'est le problème d'anniversaire .

Si vous avez 32 bits, il vous suffit que 77 163 valeurs aient plus de 50% de chances d'être dupliquées. Essaye le:

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

Maintenant, 128 bits, c’est beaucoup, vous parlez donc toujours d’un grand nombre d’éléments, ce qui vous laisse peu de risque de collision. Vous auriez besoin du nombre suivant d'enregistrements pour la cote donnée en utilisant une approximation:

  • 0,8 milliard de milliards de dollars pour un chance sur 1 000 qu'une collision se produise
  • 21,7 milliards de milliards sur 50% de chances qu'une collision se produise
  • 39,6 milliards de milliards sur 90% de chances qu'une collision se produise

Il y a environ 1E14 e-mails envoyés par an, donc il faudrait environ 400 000 ans à ce niveau pour que vous ayez 90% de chances d’en avoir deux avec le même GUID, mais c’est bien différent de dire que vous devez utiliser un ordinateur 83 milliards de dollars fois l'âge de l'univers ou que le soleil devienne froid avant de trouver un doublon.

9
Jason Goemaat

Vous avez probablement des raisons de croire que l’algorithme de production de Guids ne produit pas de nombres vraiment aléatoires, mais est en réalité cyclable avec une période << 2 ^ 128.

par exemple. RFC4122 Méthode utilisée pour dériver des GUID qui fixent les valeurs de certains bits.

La preuve du cyclisme dépendra de la taille possible de la période.

Pour de petites périodes, la table de hachage de hachage (GUID) -> GUID avec remplacement lors d'une collision si les GUID ne correspondent pas (se terminent si elles sont) peut être une approche. Envisagez également de ne remplacer le produit qu’une fraction aléatoire du temps.

En fin de compte, si la période maximale entre les collisions est suffisamment grande (et n’est pas connue à l’avance), toute méthode ne donnera qu’une probabilité de trouver la collision si elle existait.

Notez que si la méthode de génération de Guids est basée sur l’horloge (voir RFC), il n’est peut-être pas possible de déterminer si des collisions existent, car soit (a) vous ne pourrez pas attendre assez longtemps pour que l’horloge tourne autour, ou (b) vous ne pouvez pas demander suffisamment de Guids dans un créneau pour forcer une collision.

Sinon, vous pourrez peut-être montrer une relation statistique entre les bits du GUID ou une corrélation de bits entre les GUID. Une telle relation pourrait rendre hautement probable que l'algorithme soit défectueux sans pouvoir nécessairement trouver une collision réelle.

Bien sûr, si vous voulez simplement prouver que Guids peut entrer en collision, alors une preuve mathématique, et non un programme, est la solution.

9
MZB

Je ne comprends pas pourquoi personne n'a mentionné la mise à niveau de votre carte graphique ... Si vous aviez un NVIDIA Quadro FX 4800 haut de gamme ou quelque chose du genre (192 cœurs CUDA), cela irait plus vite ...

Bien sûr, si vous pouviez vous permettre d'acheter quelques NVIDIA Qadro Plex 2200 S4 (avec 960 cœurs CUDA chacun), ce calcul aurait vraiment crier. Peut-être que NVIDIA serait prête à vous en prêter quelques-uns pour une "démonstration technologique" en tant que cascade de relations publiques?

Ils voudraient sûrement faire partie de ce calcul historique ...

8
Dad

Ne manquez-vous pas tous un point majeur?

Je pensais que les GUID étaient générés à l’aide de deux facteurs, ce qui les rendait assez uniques pour être uniques au monde. D'une part, ils sont configurés avec l'adresse MAC de la machine sur laquelle vous vous trouvez et deux fois, ils utilisent l'heure à laquelle ils ont été générés, plus un nombre aléatoire.

Donc, sauf si vous l'exécutez sur la machine réelle et que vous exécutez toutes vos suppositions dans le délai le plus court utilisé par la machine pour représenter une heure dans le GUID, vous ne générerez jamais le même nombre, peu importe le nombre de suppositions. prendre en utilisant l'appel système.

J'imagine que si vous connaissez la façon dont un GUID est créé, le temps nécessaire pour le deviner serait en réalité raccourci.

Tony

7
AnthonyLambert

Vous pouvez hacher les GUID. De cette façon, vous devriez obtenir un résultat beaucoup plus rapidement.

Bien sûr, exécuter plusieurs threads en même temps est également une bonne idée, car cela augmentera les chances qu'une condition de concurrence critique génère le même GUID deux fois sur des threads différents.

7
Michael Stum
  1. Allez au laboratoire de cryogénie à New York.
  2. Gelez-vous pendant environ 1990 ans.
  3. Trouvez un emploi chez Planet Express.
  4. Achetez un nouveau processeur. Construisez un ordinateur, lancez le programme et placez-le dans un endroit sûr avec une machine à mouvements pseudo-perpétuels, comme la machine Doomsday.
  5. Attendez que la machine à remonter le temps soit inventée.
  6. Aller vers le futur en utilisant la machine à voyager dans le temps. Si vous avez acheté un processeur 1YHz 128 bits, allez à 3,938,453,320 days 20 hours 15 minutes 38 seconds 463 ms 463 μs 374 ns 607 ps après avoir commencé à exécuter le programme.
  7. ...?
  8. PROFIT!!!

... Cela prend au moins 10,783,127 années, même si vous avez un processeur de 1YHz qui est 1,000,000,000,000,000 (ou 1,125,899,906,842,624 si vous préférez utiliser un préfixe binaire) fois plus rapidement que le processeur de 1 GHz.

Donc, plutôt que d’attendre que le calcul soit terminé, il serait préférable de nourrir les pigeons qui ont perdu leur maison car d’autres pigeons n ont pris leur maison. :(

Vous pouvez également attendre que l'ordinateur quantique 128 bits soit inventé. Ensuite, vous pouvez prouver que GUID n'est pas unique, en utilisant votre programme dans un délai raisonnable (peut-être).

7
JiminP

Les GUID sont de 124 bits car 4 bits contiennent le numéro de version.

6
Behrooz

Avez-vous essayé begin = begin + new BigInteger((long)1) à la place de begin ++?

4
RCIX

Si le nombre d'UUID généré est conforme à la loi de Moore, l'impression de ne jamais manquer de GUID dans un avenir prévisible est fausse.

Avec 2 ^ 128 UUID, cela ne prendra que 18 mois * Log2 (2 ^ 128) ~ = 192 ans, avant que tous les UUID ne soient épuisés.

Et je crois (sans aucune preuve statistique) ces dernières années depuis l'adoption massive d'UUID, la vitesse à laquelle nous générons UUID augmente bien plus vite que la loi de Moore. En d'autres termes, il nous reste probablement moins de 192 ans avant de devoir faire face à une crise UUID, ce qui est beaucoup plus tôt que la fin de l'univers.

Mais comme nous ne les utiliserons certainement pas d'ici la fin de 2012, nous laisserons le soin à d'autres espèces de s'inquiéter du problème.

4
Bill Yang

Je ne comprends pas la plaisanterie que vous donnez à ce type, mais le GUID n’est unique qu’en principe, je suis tombé dessus. thread parce qu’il ya un bogue dans l’émulateur WP7 qui signifie que chaque fois qu’il s’amorce, il donne le SAME GUID lors de son premier appel! Donc, là où théoriquement vous ne pouvez pas avoir de conflit, s'il y a un problème de génération de ladite interface graphique, vous pouvez obtenir des doublons

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

3
Ben

Les probabilités d'un bug dans le code générant GUID sont beaucoup plus élevées que les probabilités que l'algorithme génère une collision. Les chances d'un bogue dans votre code pour tester les GUID sont encore plus grandes. Abandonner.

3
Mark Ransom

Le programme, bien que ses erreurs, montre la preuve qu'un GUID n'est pas unique. Ceux qui essaient de prouver le contraire passent à côté de l'essentiel. Cette déclaration prouve simplement la faible implémentation de certaines des variations GUID.

Un GUID n'est pas nécessairement unique par définition, il est hautement unique par définition. Vous venez d'affiner le sens de hautement. Selon la version, l'implémentateur (MS ou autres), l'utilisation de machines virtuelles, etc., votre définition de changements importants. (voir lien dans le post précédent)

Vous pouvez raccourcir votre table 128 bits pour prouver votre argument. La meilleure solution consiste à utiliser une formule de hachage pour raccourcir la table avec les doublons, puis à utiliser la valeur complète une fois que le hachage est entré en collision et en fonction de la régénération d'un GUID. Si vous exécutez depuis différents emplacements, vous stockeriez vos paires de clés de hachage/clé complète dans un emplacement central.

Ps: Si l'objectif est simplement de générer un nombre x de valeurs différentes, créez une table de hachage de cette largeur et vérifiez simplement la valeur de hachage.

2
ydebilloez

Puisqu'une partie de la génération de Guid est basée sur l'heure de la machine actuelle, ma théorie pour obtenir un doublon de Guid est la suivante:

  1. Effectuer une nouvelle installation de Windows
  2. Créez un script de démarrage qui réinitialise l'heure au 01/01/2010 au moment du démarrage de Windows.
  3. Juste après le script de démarrage, votre application doit générer un Guid.
  4. Clonez cette installation Windows afin d'éliminer toute différence subtile pouvant survenir lors des démarrages suivants.
  5. Créez une nouvelle image du disque dur avec cette image et démarrez la machine plusieurs fois.
1
realworldcoder

Pour moi, le temps nécessaire à un seul noyau pour générer un UUIDv1 garantit que celui-ci sera unique. Même dans une situation multicœur si le générateur d’UUID ne permet de générer qu’un seul UUID à la fois pour votre ressource spécifique (gardez à l’esprit que plusieurs ressources peuvent totalement utiliser les mêmes UUID, même si cela est peu probable puisque la ressource fait partie intégrante de l’adresse), aura plus qu'assez d'UUID pour vous durer jusqu'à épuisement de l'horodatage. À quel point je doute vraiment que vous vous en souciez.

0
whardier

Voici une solution aussi:

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

Note: nécessite Qt, mais je vous garantis que si vous le laissez fonctionner assez longtemps, il pourrait en trouver un.

(Remarque: en fait, maintenant que je suis en train de regarder cela, il y a peut-être quelque chose dans l'algorithme de génération qui empêche deux uuids générés par la suite d'entrer en collision - mais j'en doute un peu).

0
Scott

La seule solution permettant de prouver que les GUID ne sont pas uniques consiste à disposer d'un pool World GUID. Chaque fois qu'un GUID est généré quelque part, il doit être enregistré auprès de l'organisation. Ou alors, nous pourrions inclure une normalisation que tous les générateurs GUID doivent enregistrer automatiquement et pour cela, une connexion Internet active est nécessaire!

0
nawfal