J'ai deux grands ensembles d'entiers et B . Chaque ensemble contient environ un million d'entrées et chaque entrée est un entier positif de 10 chiffres au maximum.
Quel est le meilleur algorithme pour calculer et B ∖ A ? En d'autres termes, comment puis-je calculer efficacement la liste des entrées de A qui ne sont pas dans B et vice versa? Quelle serait la meilleure structure de données pour représenter ces deux ensembles, pour rendre ces opérations efficaces?
La meilleure approche que je puisse trouver consiste à stocker ces deux ensembles sous forme de listes triées et à comparer chaque élément de avec chaque élément de B , de manière linéaire. Pouvons-nous faire mieux?
algorithms
data-structures
sets
user917279
la source
la source
Réponses:
Si vous êtes prêt à stocker les ensembles dans une structure de données spécialisée, vous pouvez éventuellement obtenir des complexités intéressantes.
SoitI=O(min(|A|,|B|,|AΔB|))
Ensuite, vous pouvez définir les opérations et A Δ B , chacune dans O ( I ⋅ log | A | + | B |A∪B,A∩B,A∖B AΔB temps prévu. Donc, essentiellement, vous obtenez la taille minimale des deux ensembles, ou la taille de la différence symétrique, la plus petite des deux. C'est mieux que linéaire, si la différence symétrique est petite; c'est à dire. s'ils ont une grande intersection. En fait, pour les deux opérations de différence d'ensemble que vous souhaitez, cela est pratiquement sensible à la sortie, car ensemble, elles constituent la taille de la différence symétrique.O(I⋅log|A|+|B|I)
Voir Confluently Persistent Sets and Maps par Olle Liljenzin (2013) pour plus d'informations.
la source
Un balayage linéaire est le meilleur que je sais faire, si les ensembles sont représentés sous forme de listes chaînées triées. Le temps d'exécution est .O(|A|+|B|)
Notez que vous n'avez pas besoin de comparer chaque élément de à chaque élément de B , par paire. Cela conduirait à un temps d'exécution de O ( | A | × | B | ) , ce qui est bien pire. Au lieu de cela, pour calculer la différence symétrique de ces deux ensembles, vous pouvez utiliser une technique similaire à l'opération de "fusion" dans mergesort, modifiée de manière appropriée pour omettre les valeurs communes aux deux ensembles.A B O(|A|×|B|)
Plus en détail, vous pouvez créer un algorithme récursif comme le suivant pour calculer , en supposant que A et B sont représentés sous forme de listes liées avec leurs valeurs dans l'ordre trié:A∖B A B
J'ai représenté cela en pseudo-Python. Si vous ne lisez pas Python,
A[0]
est la tête de la liste liéeA
,A[1:]
est le reste de la liste et+
représente la concaténation des listes. Pour des raisons d'efficacité, si vous travaillez en Python, vous ne voudrez probablement pas l'implémenter exactement comme ci-dessus - par exemple, il serait préférable d'utiliser des générateurs, pour éviter de créer de nombreuses listes temporaires - mais je voulais vous montrer les idées sous la forme la plus simple possible. Le but de ce pseudo-code est juste d'illustrer l'algorithme, pas de proposer une implémentation concrète.Je ne pense pas qu'il soit possible de faire mieux, si vos ensembles sont représentés sous forme de listes triées et que vous souhaitez que la sortie soit fournie sous forme de liste triée. Vous avez fondamentalement regarder tous les éléments de et B . Croquis informel de justification: s'il y a un élément que vous n'avez pas regardé, vous ne pouvez pas le produire, donc le seul cas où vous pouvez omettre de regarder un élément est si vous savez qu'il est présent à la fois dans A et B , mais comment pourriez-vous savoir qu'il est présent si vous n'avez pas regardé sa valeur?A B A B
la source
Si A et B sont de taille égale, disjoints et entrelacés (par exemple, les nombres impairs dans A et les nombres pairs dans B), alors la comparaison par paire d'éléments en temps linéaire est probablement optimale.
Si A et B contiennent des blocs d'éléments qui se trouvent exactement dans l'un ou l'autre de A ou B, ou dans les deux, il est possible de calculer la différence, l'union et l'intersection définies en temps sous-linéaire. Par exemple, si A et B diffèrent dans exactement un élément, alors la différence peut être calculée dans O (log n).
http://arxiv.org/abs/1301.3388
la source
une option consiste à utiliser des vecteurs de bits pour représenter les ensembles (où len Cette position représente la présence ou l'absence d'un élément) et les opérations de type ensemble se réduisent ensuite à des opérations binaires qui peuvent être effectuées rapidement (et sur plusieurs bits en parallèle) sur des ordinateurs numériques. dans ce casA - B = a ∧ b¯¯ où a , b sont les vecteurs de bits. l'efficacité relative de cette technique par rapport à d'autres techniques dépend également de la rareté. pour les ensembles plus denses, il peut être plus efficace que d'autres approches. bien sûr, toute l'opération est également embarrassante en parallèle, de sorte que les opérations définies peuvent être effectuées en parallèle.
la source
long
peut stocker 32 éléments ou 1byte
, 8 éléments. donc 1M d'entrées peuvent être stockées dans seulement ~ 125 Ko de RAM! le stockage peut être significativement plus efficace que les autres représentations selon la façon dont le problème est implémenté ...