web-dev-qa-db-fra.com

Moyen rapide d'étendre un ensemble si nous savons que les éléments sont uniques

J'effectue plusieurs itérations du type:

masterSet=masterSet.union(setA)

Au fur et à mesure que l'ensemble grandit, le temps requis pour effectuer ces opérations augmente (comme on pouvait s'y attendre, je suppose).

Je suppose que le temps est pris de vérifier si chaque élément de setA est déjà dans masterSet?

Ma question est la suivante: si je SAIS que masterSet ne contient déjà aucun des éléments de setA, puis-je le faire plus rapidement? 

[METTRE À JOUR]

Étant donné que cette question suscite encore l’opinion, j’ai pensé éclaircir quelques points parmi les commentaires et les réponses ci-dessous: 

Lors des itérations, il y avait beaucoup d'itérations où je savais setA serait distinct de masterSet en raison de la façon dont il avait été construit (sans avoir à traiter de chèques) mais quelques itérations me demandaient la vérification de l'unicité.

Je me suis demandé s'il y avait un moyen de «dire» à la procédure masterSet.union() de ne pas s'embarrasser de la vérification de l'unicité cette fois-ci, car je sais que celle-ci est différente de masterSet. Perhpas en appelant une procédure ".unionWithDistinctSet()" différente 

Je pense que les réponses ont suggéré que ce n’est pas possible (et que les opérations vraiment définies devraient être assez rapides de toute façon), mais d’utiliser masterSet.update(setA) au lieu de union comme légèrement plus rapide encore. 

J'ai accepté la réponse la plus claire allant dans ce sens, résolu le problème que j'avais à l'époque et poursuivi ma vie mais aimerais toujours savoir si mon hypothèse .unionWithDistinctSet() pourrait exister?

22
Stewart_R

Vous pouvez utiliser set.update pour mettre à jour votre jeu principal. Cela évite d'allouer tout le temps un nouvel ensemble, il devrait donc être un peu plus rapide que set.union... 

>>> s = set(range(3))
>>> s.update(range(4))
>>> s
set([0, 1, 2, 3])

Bien sûr, si vous faites cela en boucle:

masterSet = set()
for setA in iterable:
    masterSet = masterSet.union(setA)

Vous obtiendrez peut-être un gain de performances en effectuant quelque chose comme:

masterSet = set().union(*iterable)

En fin de compte, le test d'adhésion d'un ensemble est O(1) (dans le cas moyen), aussi, le fait de vérifier si l'élément est déjà contenu dans l'ensemble n'est pas vraiment un gros problème de performance.

41
mgilson

Si vous savez que vos éléments sont uniques, un ensemble n'est pas nécessairement la meilleure structure.

Une simple liste est bien plus rapide à étendre.

masterList = list(masterSet)
masterList.extend(setA)
6
njzk2

Comme le souligne mgilson, vous pouvez utiliser update pour mettre à jour un ensemble in-situ à partir d'un autre. Cela fonctionne un peu plus vite:

def union():
    i = set(range(10000))
    j = set(range(5000, 15000))
    return i.union(j)

def update():
    i = set(range(10000))
    j = set(range(5000, 15000))
    i.update(j)
    return i

timeit.Timer(union).timeit(10000)   # 10.351907968521118
timeit.Timer(update).timeit(10000)  # 8.83384895324707
4
Daniel Roseman

Bien sûr, l’omission de cette vérification pourrait représenter une économie importante lorsque la méthode __eq__(..) est très coûteuse. Dans l'implémentation CPython, __eq__(..) est appelé avec tous les éléments du jeu contenant déjà le même nombre. (Référence: code source pour set .)

Cependant, cette fonctionnalité n'existera jamais dans un million d'années, car elle ouvre un autre moyen de violer l'intégrité d'un ensemble. Les problèmes associés à ce problème dépassent de loin le gain de performance (généralement négligeable). Si cela est considéré comme un goulot d'étranglement des performances, il n'est pas difficile d'écrire une extension C++ et d'utiliser son STL <set>, qui devrait être plus rapide d'un ou plusieurs ordres de grandeur.

0
Evgeni Sergeev