Qualcuno ha effettivamente implementato un Fibonacci-Heap in modo efficiente?


151

Qualcuno di voi ha mai implementato un heap di Fibonacci ? L'ho fatto qualche anno fa, ma molti ordini di grandezza erano più lenti rispetto all'utilizzo di BinHeaps basati su array.

Allora, l'ho considerata una lezione preziosa su come la ricerca non è sempre buona come sostiene di essere. Tuttavia, molti articoli di ricerca sostengono i tempi di esecuzione dei loro algoritmi basati sull'uso di un Fibonacci-Heap.

Sei mai riuscito a produrre un'implementazione efficiente? O hai lavorato con set di dati così grandi che l'Heap di Fibonacci era più efficiente? In tal caso, alcuni dettagli sarebbero apprezzati.


25
Non hai imparato che questi algoritmi nascondono sempre le loro enormi costanti dietro il loro grande grande oh ?! :) Sembra in pratica, il più delle volte, che "n" non si avvicini mai nemmeno a "n0"!
Mehrdad Afshari,

Lo so adesso. L'ho implementato la prima volta che ho ottenuto la mia copia di "Introduzione agli algoritmi". Inoltre, non ho scelto Tarjan per qualcuno che avrebbe inventato un'inutile struttura di dati, perché i suoi Splay-Tree sono in realtà piuttosto interessanti.
mdm,

mdm: Naturalmente non è inutile, ma proprio come l'ordinamento per inserzione che batte quicksort in piccoli set di dati, gli heap binari potrebbero funzionare meglio a causa di costanti più piccole.
Mehrdad Afshari,

1
In realtà, il programma per il quale avevo bisogno dell'heap stava trovando alberi Steiner per il routing nei chip VLSI, quindi i set di dati non erano esattamente piccoli. Ma al giorno d'oggi (ad eccezione di cose semplici come l'ordinamento) userei sempre l'algoritmo più semplice fino a quando non "si rompe" sul set di dati.
mdm,

1
La mia risposta è in realtà "sì". (Beh, il mio coautore su un foglio lo ha fatto.) Non ho il codice in questo momento, quindi riceverò maggiori informazioni prima di rispondere effettivamente. Osservando i nostri grafici, tuttavia, noto che i cumuli F fanno meno confronti rispetto ai b cumuli. Stavi usando qualcosa in cui il confronto era economico?
A. Rex,

Risposte:


136

Le librerie Boost C ++ includono un'implementazione di heap di Fibonacci in boost/pending/fibonacci_heap.hpp. A quanto pare questo file esiste pending/da anni e dalle mie proiezioni non sarà mai accettato. Inoltre, ci sono stati dei bug in quell'implementazione, che sono stati corretti dalla mia conoscenza e dal bravissimo Aaron Windsor. Sfortunatamente, la maggior parte delle versioni di quel file che ho trovato online (e quella nel pacchetto libboost-dev di Ubuntu) aveva ancora i bug; Ho dovuto estrarre una versione pulita dal repository Subversion.

Dalla versione 1.49 Boost le librerie C ++ hanno aggiunto molte nuove strutture heap, incluso l'heap fibonacci.

Sono stato in grado di compilare dijkstra_heap_performance.cpp rispetto a una versione modificata di dijkstra_shortest_paths.hpp per confrontare heap di Fibonacci e heap binari. (Nella riga typedef relaxed_heap<Vertex, IndirectCmp, IndexMap> MutableQueue, relaxedpassa a fibonacci.) Ho prima dimenticato di compilare con ottimizzazioni, nel qual caso Fibonacci e heap binari si comportano allo stesso modo, con gli heap di Fibonacci che di solito superano di un importo insignificante. Dopo aver compilato con ottimizzazioni molto forti, i cumuli binari hanno avuto un enorme incremento. Nei miei test, i mucchi di Fibonacci hanno sovraperformato i cumuli binari solo quando il grafico era incredibilmente grande e denso, ad esempio:

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.

Per quanto ho capito, ciò tocca le differenze fondamentali tra i cumuli di Fibonacci e i cumuli binari. L'unica vera differenza teorica tra le due strutture di dati è che i cumuli di Fibonacci supportano il tasto di riduzione nel tempo costante (ammortizzato). D'altra parte, gli heap binari ottengono grandi prestazioni dalla loro implementazione come array; l'utilizzo di una struttura di puntatore esplicita significa che i cumuli di Fibonacci subiscono un enorme impatto sulle prestazioni.

Pertanto, per beneficiare in pratica dei cumuli di Fibonacci , è necessario utilizzarli in un'applicazione in cui i metodi diminuzioni sono incredibilmente frequenti. In termini di Dijkstra, ciò significa che il grafico sottostante è denso. Alcune applicazioni potrebbero essere intrinsecamente diminuire_intenso-chiave; Volevo provare l'algoritmo di taglio minimo Nagomochi-Ibaraki perché a quanto pare genera un sacco di tasti di riduzione, ma è stato uno sforzo eccessivo per far funzionare un confronto di temporizzazione.

Attenzione : potrei aver fatto qualcosa di sbagliato. Potresti provare a riprodurre questi risultati da solo.

Nota teorica : il miglioramento delle prestazioni degli heap di Fibonacci su dimin_key è importante per le applicazioni teoriche, come il tempo di esecuzione di Dijkstra. I cumuli di Fibonacci superano anche i cumuli binari per inserimento e fusione (entrambi ammortizzati a tempo costante per i cumuli di Fibonacci). L'inserimento è essenzialmente irrilevante, perché non influisce sul tempo di esecuzione di Dijkstra ed è abbastanza facile modificare gli heap binari per avere anche l'inserimento in un tempo costante ammortizzato. Unire a tempo costante è fantastico, ma non pertinente per questa applicazione.

Nota personale : un mio amico e una volta abbiamo scritto un documento in cui spiegava una nuova coda prioritaria, che tentava di replicare il tempo (teorico) di funzionamento dei cumuli di Fibonacci senza la loro complessità. Il documento non è mai stato pubblicato, ma il mio coautore ha implementato heap binari, heap di Fibonacci e la nostra coda di priorità per confrontare le strutture di dati. I grafici dei risultati sperimentali indicano che Fibonacci ha accumulato cumuli binari leggermente superati in termini di confronti totali, suggerendo che i cumuli di Fibonacci avrebbero prestazioni migliori in una situazione in cui il costo del confronto supera le spese generali. Sfortunatamente, non ho il codice disponibile e presumibilmente nel confronto della tua situazione è economico, quindi questi commenti sono rilevanti ma non direttamente applicabili.

Per inciso, consiglio vivamente di provare ad abbinare il tempo di esecuzione degli heap di Fibonacci con la propria struttura di dati. Ho scoperto che ho semplicemente reinventato il mucchio di Fibonacci da solo. Prima pensavo che tutte le complessità dei cumuli di Fibonacci fossero idee casuali, ma in seguito mi sono reso conto che erano tutte naturali e abbastanza forzate.


Grazie! Questa domanda mi era rimasta impressa per molto tempo. In realtà ho implementato Dijkstra usando Fibonacci-Heaps prima di tentare Steiner-Trees. Tuttavia, sembra che i miei grafici fossero molto meno densi rispetto al tuo esempio. Avevano milioni di nodi, ma un grado medio di soli 5-6.
mdm,

Le prestazioni di Fib Heap sono prevedibili tramite la sequenza delle operazioni. Ho scritto un algoritmo pesante per Heap che è finito più velocemente con Fib Heap (vs. Bin Heap). Il trucco consisteva nel raggruppare il lavoro. Indipendentemente dalla frequenza di qualsiasi operazione, la differenza sta qui: DecreaseKey - ExtractMin - DecreaseKey - ExtractMin vs. DecreaseKey - DecreaseKey - ExtractMin - ExtractMin (continua sotto)
Gaminic

Quest'ultimo sarà all'incirca due volte più veloce, perché il 2 ° ExtractMin è quasi gratuito. Il nostro algoritmo estrae un batch di elementi Min di cui molti vengono scartati; uno spreco su un Bin Heap, ma meglio su un Fib Heap. Purtroppo questo non si riflette chiaramente nella complessità temporale che le persone forniscono quando parlano di algoritmi basati su Heap. Con i limiti ammortizzati, la complessità totale non è semplicemente # operazioni * complessità dell'operazione.
Gaminic,

1
Qualche possibilità di provare anche ad accoppiare heap e / o heap rilassati?
Thomas Ahle,

1
Non sono sicuro del motivo per cui i tuoi risultati sono apparsi così vicini, ho usato STL priority_queue vs heap fibonacci auto-implementato e l'heap binario era dietro di un ampio margine nei miei test .
Nicholas Pipitone,

33

Nel 1993 Knuth fece un paragone tra mucchio di fibonacci e cumuli binari per la minima estensione degli alberi nel suo libro Stanford Graphbase . Scoprì che i fibonacci erano da 30 a 60 precentivamente più lenti dei cumuli binari alle dimensioni dei grafici che stava testando, 128 vertici a densità diverse.

Il codice sorgente è in C (o meglio CWEB che è un incrocio tra C, matematica e TeX) nella sezione MILES_SPAN.


1

disconoscimento

So che i risultati sono abbastanza simili e "sembra che il tempo di esecuzione sia totalmente dominato da qualcosa di diverso dall'heap" (@Alpedar). Ma non ho trovato alcuna prova di ciò nel codice. Il codice è aperto, quindi se riesci a trovare qualcosa che possa influenzare il risultato del test, per favore dimmelo.


Forse ho fatto qualcosa di sbagliato, ma ho scritto un test , basato sul confronto tra A.Rex anwser:

  • Fibonacci-Heap
  • D-Ary-heap (4)
  • Binary-Heap
  • Rilassato-Heap

I tempi di esecuzione (solo per i grafici completi) per tutti i cumuli erano molto vicini. Il test è stato realizzato per grafici completi con 1000,2000,3000,4000,5000,6000,7000 e 8000 vertici. Per ogni test sono stati generati 50 grafici casuali e l'output è il tempo medio di ciascun heap:

Mi dispiace per l'output, non è molto dettagliato perché ne avevo bisogno per costruire alcuni grafici da file di testo.


Ecco i risultati (in secondi):

tabella dei risultati heap


4
Quanti spigoli ci sono in ciascun caso? E quale algoritmo stai eseguendo esattamente? I tuoi risultati non hanno senso se non sappiamo con cosa abbiamo a che fare.
Kokx,

Come mi dispiace, tutti i grafici sono completi, quindi puoi calcolare il numero di spigoli per ogni caso. Cosa intendi con "corri esattamente". Sono in testa ai tavoli.
Guilherme Torres Castro,

22
Sembra che il tempo di esecuzione sia totalmente dominato da qualcosa di diverso dall'heap (potrebbe essere la generazione di un grafico o di un IO). Questi risultati quasi esattamente uguali sono incredibili.
Alpedar,

2
Beh, forse il tempo è dominato da qualcos'altro, ma sono sicuro che non sia l'IO o la generazione dei grafici. Ad ogni modo il codice sorgente è disponibile e sarò molto contento se qualcuno troverà un errore e correggerà il messaggio.
Guilherme Torres Castro,

Questi test sembrano misurare qualcosa di completamente diverso. Potresti commentare il test che hai eseguito? Ricorda che il problema del percorso più breve in un grafico completo è O (1) se le distanze sono Geometriche / Euclide.
Gaminic,

0

Ho anche fatto un piccolo esperimento con il mucchio di Fibonacci. Ecco il link per i dettagli: Experimenting-with-dijkstras-algoritmo . Ho appena cercato su Google i termini "Fibonacci heap java" e ho provato alcune implementazioni open source esistenti dell'heap Fibonacci. Sembra che alcuni di loro abbiano qualche problema di prestazioni, ma ce ne sono alcuni abbastanza buoni. Almeno, stanno battendo le prestazioni PQ heap ingenuo e binario nel mio test. Forse possono aiutare a implementare quello efficiente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.