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 #.
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.
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).
Un GUID est théoriquement non unique. Voici votre preuve:
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.
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.
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.
Personnellement, je pense que le "Big Bang" a été provoqué par la collision de deux GUID.
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();
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.
[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"
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.
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; -)
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!
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.
Si les collisions GUID posent un problème, je vous recommande d'utiliser plutôt ScottGuID .
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:
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.
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.
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 ...
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
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.
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.... 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).
Les GUID sont de 124 bits car 4 bits contiennent le numéro de version.
Avez-vous essayé begin = begin + new BigInteger((long)1)
à la place de begin ++?
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.
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
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.
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.
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:
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.
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).
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!