10
votes

La plus grande complexité de la création d'un hashset d'une collection

J'ai une collection de valeurs int avec lesquelles je peuploie un hashset de la manière suivante - xxx

supposant qui itérant le ienumerable est o (n) , quel sera le pire des cas la complexité de la création d'un hashset de telle manière?


0 commentaires

3 Réponses :


5
votes

Vous pouvez apporter le pire des cas à O (n ^ 2) en fournissant des objets que tous les hachages au même godet lorsque l'ensemble atteint sa taille maximale . Par exemple, si vous passez une séquence de 17519 INT S est construite comme xxx

pour i entre 1 et 17519 inclus, tous les numéros hachage au seau initial sur la mise en œuvre de Microsoft de hashset , prise o (n ^ 2) pour insérer: xxx

Définissez un koint Brea Kpoint et examinez H dans le débogueur. Regardez sur la vue brute / membres non publics / m_buckets. Observez que le godet initial comporte 17519 éléments, tandis que les 17518 restants ont tous des zéros.


6 commentaires

Je ne serais pas surpris si c'est O (n ^ 2)


Mais qu'en est-il de la complexité non amortie et pire des cas?


Vous pouvez forcer le pire que O (N ^ 2) temps si vous supposez une heure personnalisée avec un «code> gethashcode pauvre ou malveillant . Vous pouvez avoir un gethashcode qui ne revient jamais, par exemple, et ne jamais être en mesure de remplir la tâche ou que vous pourriez avoir une méthode gethashcode qui prend o ( n ^ 2) Time pour calculer, rendant ainsi les méthodes HASHSET ... pire que cela.


@Servy Mon point est que, puisque vous n'avez pas de contrôle sur <-code> gethascode de int32 , vous ne pouvez pas forcer nouveau hashset (myIénumérable) de l'OP dans le O (n ^ 2) territoire. Lorsque vous avez le contrôle sur gethashcode , vous pouvez forcer hashset pour bloquer indéfiniment :) hashset est le milieu de la route : Le pire que vous puissiez faire est o (n ^ 2) en fournissant une séquence particulièrement mauvaise pour la mise en œuvre .NET de int64.gethashcode .


Pour int s Vous pouvez toujours créer des collisions de l'index du godet. Ajoutez simplement des INT qui sont un multiple de la capacité . J'attends des performances d'addition O (n ^ 2) dans un tel scénario, mais je suis trop paresseux pour comprendre les capacités préférées de hashset .


@CODESINCHAOS Vous avez raison, vous pouvez forcer o (n ^ 2) . Je n'ai pas réalisé qu'il suffit de considérer la dernière taille, pensant que vous devez essayer 3, 7, 17, 37, 89, etc. Merci pour le conseil!



8
votes

La documentation indique réellement:

Ce constructeur est une opération O (n), où n est le nombre de éléments du paramètre de collection.

http://msdn.microsoft.com/en-us/library/ BB301504.aspx


13 commentaires

Mais est-ce le pire complexité des cas ou une complexité amortie?


@Ughsegment Vous voulez dire "la moyenne" complexité non "amortie". "Amortized" est utilisé pour les opérations parfois chères (par exemple, un doublement du magasin de support) et bon marché pour le reste. Ce concept est orthogonal à la moyenne par rapport au pireCasse.


@UGHSEGEMENT À AJOUTER À LA RÉPONSE DE CODEIPHAOS, c'est à la fois le pire des cas et la complexité amortise. (Étant donné qu'il a expliqué pourquoi il est possible que ce soit les deux, je déclare que c'est en fait le cas ici.)


@Servy je comprends que les deux peuvent être les mêmes; Mais cela n'affecte pas vraiment ma question - est le le pire des cas la complexité o (n) ?


Non, en général, le pire des cas est quadratique bien sûr, mais c'est pour les objets avec la même sortie gethashcode (). Je me demande sur INT.


@UGHSEGEMENT Selon vous, quel est le pire des cas? L'algorithme vérifie le premier élément de myInumérable , vérifie si c'est déjà dans le dictionnaire (qui est en temps constant), et sinon, l'ajoute. Ensuite, vérifie le deuxième élément de myInumérable , voir si elle est déjà dans le dictionnaire et le ajoute autrement. Selon vous, quel est le pire des cas? Peut-être que tous les éléments sont distincts, mais les codes de hachage entrent en collision à chaque fois? Essayez de faire une classe qui remplace gethascode () pour toujours renvoyer 0 et remplace égaux (objet) pour renvoyer referequals . Puis mesurez vous-même!


@Jeppestignielsen Ma question est spécifiquement sur la plus grande complexité des cas de hashset , pas de hashsets en général.


@Unghsegment Comme les personnes ont écrit ailleurs sur cette page, pour un int , la méthode gethashcode () renvoie simplement ceci . Par exemple, (- 987654321) .gethashcode () == -987654321 . Par conséquent, vous JAMAIS Obtenez des collisions avec int . Selon la documentation, la surcharge du constructeur que vous utilisez des ensembles capacité à la taille requise immédiatement, alors peut-être que le hashset <> ne doit pas se redresser. Dans ce cas, je soupçonnerais la plus grande complexité des cas est O (n).


@Jeppestignielsen c'est ce que je pensais. Cependant, je pense que malgré aucune collision de hachage, il est toujours possible de produire des collisions avec l'indice des éléments dans les seaux - et c'est ce qui me fait penser que cette opération peut être supérieure à celle de O (n) dans certaines situations. Je me tromperai peut-être car je ne sais pas très bien comment hashset est implémenté dans .NET.


Bien sûr, différents Ints peuvent entrer en collision. S'il y a 7 godets, alors l'INT N et n + 7 collision. => Cette réponse ne répond pas aux pires performances des cas.


@UGHSEGEMENT Essayez de déboguer et d'inspecter les champs privé du hashset <> avec le débogueur. Vous verrez probablement quels "godets" ou module sont choisis. Peut-être pour une taille donnée de votre collection d'entrée, vous pouvez déterminer quels godets sont utilisés. Ensuite, faites votre collection de la même taille, mais choisissez les membres de manière perverse. Cela devrait être possible. Donc, peut-être que c'est pire que O (n) après tout, pour une entrée "mal".


@Jeppestignielsen J'ai utilisé .NET réflecteur pour savoir comment le hashset obtient la valeur du module qu'il utilise dans le calcul du hachage. J'ai utilisé ces informations pour fournir au constructeur avec différentes valeurs qui tombent dans le même indice et la dégradation des performances de mes tests semblait être presque parfaitement quadratique. Il semble après tout ce que la plus grande complexité des cas est effectivement O (n ^ 2) , même sans collision dans les valeurs de hachage.


@UGHSEGEMENT vraiment cool. Les codesInchaos prévoyaient que dans son commentaire à l'une des autres réponses.



2
votes

Une expérience rapide avec un code de hashcode dégénéré (une constante) montre qu'il est quadratique. xxx

sorties: xxx

maintenant

maintenant Certaines affirment que vous n'obtenez pas de collisions multiples du Hashcode pour INTS. Bien que cela soit techniquement vrai, ce qui compte pour la performance n'est pas une collision du hashcode, mais une collision de l'indice de seau. Je pense que hashset utilise quelque chose comme godet = (hash & 0x7FFFFFF)% capacité . Donc, si vous ajoutez une séquence d'entiers qui est un multiple de taille de godet préférée, il sera toujours très lent.


4 commentaires

Si tous les objets renvoient le même hashcode, c'est OUI, c'est O (n * N), en raison de collisions. Mais la question de l'OP était sur la collecte de INT. Je me demande donc à quel point il serait difficile de choisir une paire d'Int avec des hachices égaux.


Je ne pense pas que le test que vous avez effectué est le même que ce que j'ai décrit dans ma question. Je suis spécifiquement intéressé par la pire complexité de la réussite d'une collection avec une quantité connue d'éléments au constructeur , pas la complexité de plusieurs Ajouter appels.


@Sergeys int est l'une des personnes à une poignée de types qui a nul collisions. Le nombre de valeurs possibles int n'est pas plus grande que le nombre de valeurs possibles int int , de sorte que le code de hachage pour int est en fait unique pour différentes valeurs. (En d'autres termes, son code de hachage peut simplement se revenir.) Autres types tels que octet et Char ont également moins de valeurs que int et ainsi ne fera jamais entrer en collision.


Même avec elle, il est possible de causer des collisions de l'indice de seau. C'est juste plus gênant de sortir. | @UGHSEGEMENT C'est la même chose avec le constructeur. Voir le code mis à jour.