Bon intersection d'une collection de jeux en C++
J'ai une collection de std::set
. Je veux trouver l'intersection de tous les ensembles dans cette collection, le plus rapidement. Le nombre d'ensembles dans la collection est généralement très faible (~5-10), et le nombre d'éléments dans chaque jeu est est généralement de moins de 1000, mais peut parfois aller jusqu'à environ 10000. Mais j'ai besoin de faire ces intersections des dizaines de milliers de fois, aussi vite que possible. J'ai essayé de référence quelques méthodes comme suit:
- En place d'intersection dans un
std::set
objet qui, initialement, les exemplaires de la première série. Alors pour la suite des séries, on itère sur tous les élément de lui-même et l'ith ensemble de la collection, et supprime les éléments de lui-même, en tant que de besoin. - À l'aide de
std::set_intersection
dans un temporairestd::set
, swap de contenu pour un jeu actuel, puis de nouveau trouver l'intersection de l'ensemble actuel avec le jeu suivant et insérez-la dans le temp ensemble, et ainsi de suite. - Manuellement itérer sur tous les éléments de toutes les séries comme dans le 1), mais à l'aide d'un
vector
que le conteneur de destination au lieu destd::set
. - Même que dans le 4, mais en utilisant une
std::list
au lieu d'unvector
, soupçonnant unlist
permet d'avoir plus rapidement des suppressions dans le milieu. - À l'aide de hachage de jeux (
std::unordered_set
) et la vérification de tous les éléments de tous les ensembles.
Comme il s'est avéré, à l'aide d'un vector
est légèrement plus rapide lorsque le nombre d'éléments dans chaque set est petit, et list
est légèrement plus rapide pour les grands ensembles. En place à l'aide de set
est considérablement plus lent que les deux, suivie par set_intersection
de hachage et de jeux. Est-il un algorithme plus rapide/discbased/astuces pour accomplir cela? Je peux poster des extraits de code si nécessaire. Merci!
std::unordered_map
et de compter le nombre d'occurrences de chacun des éléments. Il est O(N) dans le nombre total d'éléments. Ensuite, vous choisissez simplement les éléments qui ont un total égal au nombre de jeux, O(M) est le nombre d'éléments distincts. Aucune idée de comment il pourrait effectuer.Je vois. Je vais opter pour cette solution, bien que je soupçonne, il ne sera pas plus rapide qu'un
std::list
en raison de hachage et autres frais généraux. Merci!Cette méthode va donner le jeu en non trié. Heureusement, j'ai deux cas d'utilisation, qui nécessite le résultat dans l'ordre de tri, et une qui ne l'est pas. Si cette méthode est assez rapide, je peux l'utiliser au moins pour le cas où l'intersection n'est pas nécessaire d'être triés.
J'ai essayé cette approche, et pour mes données, ce n'était que légèrement plus vite que mon approche 5 (en utilisant
unordered_set
).Vous pouvez essayer cette idée. Pire des cas linéaire (ne peut pas éviter que, si les jeux ont pour la plupart les mêmes éléments), mais si l'intersection est petit, il peut être beaucoup plus rapide.
OriginalL'auteur Paresh | 2012-10-13
Vous devez vous connecter pour publier un commentaire.
Vous pourriez vouloir essayer une généralisation de
std::set_intersection()
: l'algorithme est d'utiliser des itérateurs pour tous les jeux:end()
de sa correspondante, vous avez terminé. Ainsi, on peut supposer que tous les itérateurs sont valides.x
.std::find_if()
le premier élément au moins aussi important quex
.x
en faire le nouveau candidat de la valeur et de la recherche de nouveau dans la séquence des itérateurs.x
vous avez trouvé un élément de l'intersection: l'Enregistrer, de la incrément de tous les itérateurs de début plus de.std::find_if
lorsque l'on travaille avecstd::set
, après tout,std::set
caractéristiques à la foisstd::lower_bound
etstd::upper_bound
avec sont généralement plus rapide.pas dans ce cas,
find_if
, en moyenne, de ne jamais avoir à l'avance de plus de deux éléments et est donc S (1), tandis que???er_bound
est S (log n).Évidemment, cela dépend de l'interface de l'algorithme, et je voudrais les faire fonctionner sur une séquence de paires d'entrée itérateurs:
std::set_intersection()
fait aussi bien. Fait intéressant, je pense que la complexité de votre approche suggérée est O (n log n) * m) : oùn
est la taille maximale des décors et desm
est le nombre de jeux. Mon algorithme a une complexité de O(n * m). Je pense que mon approche de la gagne.Merci! Je ne comprenais pas pourquoi
find_if
, en moyenne, de ne jamais avoir à l'avance de plus de deux éléments?comme Paresh je me demande où les 2 éléments viennent (j'ai peut-être raté quelque chose d'évident). Il me semble qu'il dépendrait de la façon dont les données sont distribuées, ne serait-il pas ? Par exemple supposons que j'ai un ensemble de 100 éléments et un autre de 1000 éléments couvrant la même gamme. Ensuite, dans la moyenne, j'ai besoin de sauter sur les 10 éléments de l'ensemble à chaque étape.
OriginalL'auteur Dietmar Kühl
De nuit est un bon conseiller et je pense que j'ai peut-être une idée 😉
C'est pourquoi, où les vitesses de question, un
vector
(ou peut-être undeque
sont si grandes structures: ils jouent très bien avec de la mémoire. En tant que tel, je recommanderais certainement à l'aide devector
que nos structures intermédiaires; bien que les soins doivent être prises pour ne jamais insérer/supprimer à partir d'une extrémité pour éviter les délocalisations.Alors j'ai pensé à une approche assez simple:
Il semble correct, je ne peux pas garantir sa vitesse bien que, évidemment.
vector
comme intermédiaire conteneur, tout comme vous avez fait. La différence étant que vous avez utilisé laset_intersection
, qui nécessite deuxvectors
, tandis que je continuais à 1 vecteur, avec l'inconvénient que j'ai eu à effacer dans le milieu. Même si votre approche doit idéalement avoir été plus rapide, je suppose que le complexe de facteurs comme la mémoire contiguë, la mise en cache (tableau 1 vs 2), etc font de ce plus lent que les options 3 et 4 que j'ai essayé ci-dessus. Bien sûr, le kilométrage peut varier en fonction des données.+1 pour penser en termes de mémoire et de mise en cache, et de donner une bonne explication! Comme une note de côté, je suis envisage d'utiliser des vecteurs au lieu de std::set, et de l'insérer dans l'ordre de tri dans les vecteurs si c'est comparable. La compacité peut raisonnablement rapide, et les intersections serait certainement plus rapide.
OriginalL'auteur Matthieu M.