L'union de couverture est-elle toujours aussi rapide que diviser pour mieux régner?

8

Adams décrit un algorithme de division et de conquête pour trouver l'union de deux ensembles (représentés sous forme d'arbres de recherche binaires pondérés). Il décrit ensuite un nouvel algorithme de "l'union de couverture" qui, selon lui, améliore celui de diviser pour mieux régner. Cependant, il n'offre pas de preuve, ni même de véritable explication, de la raison pour laquelleO(m+n), et encore moins pourquoi il devrait être plus rapide que diviser pour mieux régner.

Blelloch, Ferizovic et Sun montrent que l'algorithme de division et de conquête d'Adams atteint en fait l'optimalité théoriqueΘ(mlog(n/m+1))mn. Ils ne traitent cependant pas de l'algorithme de l'union de couverture.

L'union de couverture est-elle en fait aussi efficace que le diviser pour mieux régner? La partie la moins évidente est la garniture intérieure. Il semble, au moins superficiellement, dupliquer le travail entre les sous-arbres gauche et droit que le partage complet partage entre eux. C'est peut-être bien pour une raison quelconque, mais je ne sais pas pourquoi.

Une autre enquête: Haskell Data.Setet Data.Maputiliser des variantes de haies d'intersection et de différence, ainsi que l'union. Je n'ai trouvé aucune discussion publiée sur ces algorithmes. Des questions similaires s'appliquent également à celles-ci.

dfeuer
la source

Réponses:

3

Bien que je n'aie pas encore vu ou produit une analyse théorique des algorithmes de couverture, j'ai des preuves empiriques qu'ils sont pires que les algorithmes de division et de conquête pour les arbres binaires.

En commençant par le code du containerspackage Haskell , j'ai optimisé l'algorithme de l'union de couverture en appliquant manuellement la spécialisation du modèle d'appel pour réduire l'allocation intermédiaire. Cela a amélioré ses performances d'environ 10%, ce qui lui donne un coup franc.

En commençant par le code de division et de conquête dans Adams, j'ai optimisé l'algorithme d'union en ajoutant des cas spéciaux lorsque l'une des entrées est un singleton (le code d'union de couverture optimise ainsi un côté, et il n'est pas clair si l'autre côté peut être optimisé De même).

J'ai testé chaque implémentation à l'aide d'une collection de tests de performances d'ensemble fournis avec containers. Diviser pour mieux régner était généralement plus rapide que la haie, parfois deux fois plus rapide. Quand il était plus lent, il ne l'était que légèrement.

Des repères similaires pour d'autres opérations d'ensemble ont donné des résultats similaires.


Spéculation:

Les algorithmes de haie peuvent être utiles lors de l'utilisation d'arbres avec de grands facteurs de ramification, qui peuvent être plus coûteux à fractionner récursivement. Ils peuvent également être utiles pour les petits sous-arbres, où ils peuvent économiser suffisamment d'allocation pour valoir le travail supplémentaire.

dfeuer
la source
Avez-vous réellement modifié l'implémentation en Data.Setfonction de ces observations?
Joachim Breitner
@JoachimBreitner, oui, je l'ai fait. J'ai également utilisé la même approche pour les nouveaux utilitaires de fusion sécurisée, bien que caractériser leurs caractéristiques de performance précises soit sûrement trop difficile à gérer.
dfeuer