web-dev-qa-db-fra.com

Quelqu'un a-t-il réellement mis en œuvre un Fibonacci-Heap efficacement?

Est-ce que quelqu'un d'entre vous a déjà implémenté un Fibonacci-Heap ? Je l'ai fait il y a quelques années, mais c'était plusieurs ordres de grandeur plus lent que l'utilisation de BinHeaps basés sur des baies.

À l'époque, je pensais que c'était une leçon précieuse sur la façon dont la recherche n'est pas toujours aussi bonne qu'elle prétend l'être. Cependant, de nombreux articles de recherche affirment les temps d'exécution de leurs algorithmes basés sur l'utilisation d'un tas de Fibonacci.

Avez-vous déjà réussi à produire une implémentation efficace? Ou avez-vous travaillé avec des ensembles de données si volumineux que le Fibonacci-Heap était plus efficace? Si oui, certains détails seraient appréciés.

148
mdm

Les Boost C++ bibliothèques incluent une implémentation des tas de Fibonacci dans boost/pending/fibonacci_heap.hpp. Ce fichier a apparemment été dans pending/ pendant des années et par mes projections ne sera jamais acceptée. De plus, il y a eu des bugs dans cette implémentation, qui ont été corrigés par ma connaissance et le mec cool Aaron Windsor. Malheureusement, la plupart des versions de ce fichier que j'ai pu trouver en ligne (et celle du paquet libboost-dev d'Ubuntu) contenaient toujours les bogues; J'ai dû extraire ne version propre du dépôt Subversion.

Depuis la version 1.49Boost C++ bibliothèques a ajouté beaucoup de nouvelles structures de tas, y compris le tas fibonacci.

J'ai pu compiler dijkstra_heap_performance.cpp contre une version modifiée de dijkstra_shortest_paths.hpp pour comparer les tas de Fibonacci et les tas binaires. (Dans la ligne typedef relaxed_heap<Vertex, IndirectCmp, IndexMap> MutableQueue, changez relaxed en fibonacci.) J'ai d'abord oublié de compiler avec des optimisations, auquel cas Fibonacci et les tas binaires fonctionnent à peu près de la même manière, les tas Fibonacci surpassant généralement d'une quantité insignifiante. Après avoir compilé avec des optimisations très fortes, les tas binaires ont reçu un énorme coup de pouce. Dans mes tests, les tas de Fibonacci n'ont surpassé les tas binaires que lorsque le graphique était incroyablement grand et dense, par exemple:

Generating graph...10000 vertices, 20000000 edges.
Running Dijkstra's with binary heap...1.46 seconds.
Running Dijkstra's with Fibonacci heap...1.31 seconds.
Speedup = 1.1145.

Autant que je sache, cela touche aux différences fondamentales entre les tas de Fibonacci et les tas binaires. La seule vraie différence théorique entre les deux structures de données est que les tas de Fibonacci prennent en charge la clé de diminution en temps constant (amorti). D'un autre côté, les tas binaires obtiennent beaucoup de performances de leur implémentation en tant que tableau; l'utilisation d'une structure de pointeur explicite signifie que les tas de Fibonacci subissent un énorme impact sur les performances.

Par conséquent, pour bénéficier des tas de Fibonacci en pratique , vous devez les utiliser dans une application où les touches_diminution sont incroyablement fréquentes. En termes de Dijkstra, cela signifie que le graphique sous-jacent est dense. Certaines applications pourraient être intrinsèquement diminuées par une intensité de clé; Je voulais essayer l'algorithme de coupure minimale de Nagomochi-Ibaraki parce qu'apparemment il génère beaucoup de touches de diminution, mais c'était trop d'efforts pour faire fonctionner une comparaison de synchronisation.

Avertissement : J'ai peut-être fait quelque chose de mal. Vous pouvez essayer de reproduire ces résultats vous-même.

Note théorique : L'amélioration des performances des tas de Fibonacci sur diminuer_key est importante pour les applications théoriques, telles que l'exécution de Dijkstra. Les tas de Fibonacci surpassent également les tas binaires lors de l'insertion et de la fusion (tous deux amortis en temps constant pour les tas de Fibonacci). L'insertion est essentiellement hors de propos, car elle n'affecte pas l'exécution de Dijkstra, et il est assez facile de modifier les segments binaires pour avoir également l'insertion en temps constant amorti. La fusion en temps constant est fantastique, mais ne concerne pas cette application.

Note personnelle : Un de mes amis et moi avons écrit une fois un document expliquant une nouvelle file d'attente prioritaire, qui tentait de reproduire le temps d'exécution (théorique) des tas de Fibonacci sans leur complexité. L'article n'a jamais été publié, mais mon coauteur a mis en œuvre des tas binaires, des tas de Fibonacci et notre propre file d'attente prioritaire pour comparer les structures de données. Les graphiques des résultats expérimentaux indiquent que les tas de Fibonacci ont légèrement surpassé les tas binaires en termes de comparaisons totales, ce qui suggère que les tas de Fibonacci fonctionneraient mieux dans une situation où le coût de comparaison dépasse les frais généraux. Malheureusement, je n'ai pas le code disponible, et probablement dans votre situation, la comparaison est bon marché, donc ces commentaires sont pertinents mais pas directement applicables.

Soit dit en passant, je recommande fortement d'essayer de faire correspondre l'exécution des tas de Fibonacci avec votre propre structure de données. J'ai découvert que je réinventais moi-même les tas de Fibonacci. Avant, je pensais que toutes les complexités des tas de Fibonacci étaient des idées aléatoires, mais après j'ai réalisé qu'elles étaient toutes naturelles et assez forcées.

133
A. Rex

Knuth a fait une comparaison entre le tas de fibonacci et les tas binaires pour des arbres couvrant minimum en 1993 pour son livre Stanford Graphbase . Il a trouvé que les fibonacci étaient 30 à 60% plus lents que les tas binaires aux tailles de graphique qu'il testait, 128 sommets à différentes densités.

Le code source est en C (ou plutôt CWEB qui est un croisement entre C, math et TeX) dans la section MILES_SPAN.

32
paperhorse

Clause de non-responsabilité

Je sais que les résultats sont assez similaires et "on dirait que le temps d'exécution est totalement dominé par autre chose que le tas" (@Alpedar). Mais je n'ai trouvé aucune preuve de cela dans le code. Le code est ouvert, donc si vous trouvez tout ce qui peut affecter le résultat du test, dites-le moi.


J'ai peut-être fait quelque chose de mal, mais j'ai écrit un test , basé sur A.Rex réponse en comparant:

  • Fibonacci-Heap
  • D-Ary-tas (4)
  • Binaire-tas
  • Tas détendu

Les temps d'exécution (pour les graphiques complets uniquement) pour tous les tas étaient très proches. Le test a été fait pour des graphes complets avec 1000,2000,3000,4000,5000,6000,7000 et 8000 sommets. Pour chaque test, 50 graphiques aléatoires ont été générés et la sortie est le temps moyen de chaque tas:

Désolé pour la sortie, ce n'est pas très détaillé car j'en avais besoin pour construire des graphiques à partir de fichiers texte.


Voici les résultats (en secondes):

heap result table

J'ai également fait une petite expérience avec le tas de Fibonacci. Voici le lien pour les détails: Experimenting-with-dijkstras-algorithm . Je viens de googler les termes "tas de Fibonacci Java" et j'ai essayé quelques implémentations open source existantes du tas de Fibonacci. Il semble que certains d'entre eux aient des problèmes de performances, mais il y en a qui sont assez bons. Au moins, ils battent les performances PQ naïves et binaires du tas dans mon test. Peut-être qu'ils peuvent aider à mettre en œuvre la solution efficace.

0
gabormakrai