Alguém realmente implementou um Fibonacci-Heap com eficiência?

151

Alguém de vocês já implementou um Fibonacci-Heap ? Eu fiz isso alguns anos atrás, mas foi várias ordens de magnitude mais lenta do que usar BinHeaps baseados em array.

Naquela época, eu pensava nisso como uma lição valiosa de como a pesquisa nem sempre é tão boa quanto afirma ser. No entanto, muitos trabalhos de pesquisa afirmam os tempos de execução de seus algoritmos com base no uso de um Fibonacci-Heap.

Você já conseguiu produzir uma implementação eficiente? Ou você trabalhou com conjuntos de dados tão grandes que o Fibonacci-Heap foi mais eficiente? Nesse caso, alguns detalhes seriam apreciados.

mdm
fonte
25
Você não aprendeu que esses caras de algoritmo sempre escondem suas constantes enormes atrás de seus grandes oh? :) Na prática, na maioria das vezes, parece que "n" nunca chega nem perto do "n0"!
Mehrdad Afshari 02/02/09
Eu sei agora. Eu o implementei quando recebi minha cópia da "Introdução aos algoritmos". Além disso, eu não escolhi Tarjan para alguém que inventaria uma estrutura de dados inútil, porque suas Splay-Trees são realmente muito legais.
Mdm
mdm: é claro que não é inútil, mas, assim como a inserção que supera a classificação rápida em pequenos conjuntos de dados, as pilhas binárias podem funcionar melhor devido a constantes menores.
Mehrdad Afshari 02/02/09
1
Na verdade, o programa para o qual eu precisava era encontrar árvores Steiner para roteamento em chips VLSI, portanto os conjuntos de dados não eram exatamente pequenos. Mas hoje em dia (exceto para coisas simples, como classificação), eu sempre usaria o algoritmo mais simples até que "quebre" no conjunto de dados.
Mdm
1
Minha resposta para isso é realmente "sim". (Bem, meu co-autor em um artigo tinha.) Eu não tenho o código agora, então vou obter mais informações antes de realmente responder. Observando nossos gráficos, no entanto, observo que os montões F fazem menos comparações do que os montes b. Você estava usando algo em que a comparação era barata?
A. Rex

Respostas:

136

As bibliotecas Boost C ++ incluem uma implementação de hibridos de Fibonacci no Windows boost/pending/fibonacci_heap.hpp. Aparentemente, esse arquivo existe pending/há anos e, de acordo com minhas projeções, nunca será aceito. Além disso, houve erros nessa implementação, que foram corrigidos pelo meu conhecido e pelo cara legal Aaron Windsor. Infelizmente, a maioria das versões desse arquivo que eu consegui encontrar on-line (e a do pacote libboost-dev do Ubuntu) ainda tinham os bugs; Eu tive que puxar uma versão limpa do repositório Subversion.

Desde a versão 1.49, as bibliotecas Boost C ++ adicionaram muitas novas estruturas de heaps, incluindo heap de fibonacci.

Consegui compilar dijkstra_heap_performance.cpp em uma versão modificada do dijkstra_shortest_paths.hpp para comparar pilhas de Fibonacci e pilhas binárias. (Na linha typedef relaxed_heap<Vertex, IndirectCmp, IndexMap> MutableQueue, mude relaxedpara fibonacci.) Primeiro, esqueci-me de compilar com otimizações . Nesse caso, Fibonacci e pilhas binárias realizam a mesma coisa, com as pilhas de Fibonacci geralmente superando uma quantidade insignificante. Depois que compilei com otimizações muito fortes, as pilhas binárias receberam um enorme impulso. Nos meus testes, os hibridos de Fibonacci apenas superaram os binários binários quando o gráfico era incrivelmente grande e denso, por exemplo:

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.

Até onde eu entendo, isso toca nas diferenças fundamentais entre os montes de Fibonacci e os binários. A única diferença teórica real entre as duas estruturas de dados é que as pilhas de Fibonacci suportam chave decrescente no tempo constante (amortizado). Por outro lado, as pilhas binárias obtêm muito desempenho de sua implementação como uma matriz; o uso de uma estrutura explícita de ponteiros significa que as pilhas de Fibonacci sofrem um enorme impacto no desempenho.

Portanto, para se beneficiar dos montes de Fibonacci na prática , você deve usá-los em um aplicativo em que as teclas de diminuição são incrivelmente frequentes. Em termos de Dijkstra, isso significa que o gráfico subjacente é denso. Alguns aplicativos podem ser intrinsecamente intensos em key_key; Eu queria experimentar o algoritmo de corte mínimo de Nagomochi-Ibaraki porque, aparentemente, gera muitas teclas de diminuição, mas foi muito esforço para fazer uma comparação de tempo funcionar.

Aviso : Eu posso ter feito algo errado. Você pode tentar reproduzir esses resultados você mesmo.

Nota teórica : O desempenho aprimorado dos montes de Fibonacci no decréscimo-chave é importante para aplicações teóricas, como o tempo de execução de Dijkstra. As pilhas de Fibonacci também superam as pilhas binárias na inserção e mesclagem (ambas amortizadas em tempo constante pelas pilhas de Fibonacci). A inserção é essencialmente irrelevante, porque não afeta o tempo de execução do Dijkstra, e é bastante fácil modificar as pilhas binárias para também inserir no tempo constante amortizado. Mesclar em tempo constante é fantástico, mas não relevante para esta aplicação.

Nota pessoal : Um amigo meu e eu uma vez escrevemos um artigo explicando uma nova fila de prioridades, que tentou replicar o tempo de execução (teórico) dos montes de Fibonacci sem sua complexidade. O artigo nunca foi publicado, mas meu co-autor implementou pilhas binárias, pilhas de Fibonacci e nossa própria fila de prioridade para comparar as estruturas de dados. Os gráficos dos resultados experimentais indicam que os montes de Fibonacci superaram os montantes binários ligeiramente superiores em comparação total, sugerindo que os montes de Fibonacci teriam melhor desempenho em uma situação em que o custo de comparação excede a sobrecarga. Infelizmente, não tenho o código disponível e, presumivelmente, na sua comparação de situação é barato, esses comentários são relevantes, mas não são diretamente aplicáveis.

Aliás, eu recomendo tentar combinar o tempo de execução dos hibridos de Fibonacci com sua própria estrutura de dados. Eu descobri que simplesmente reinventei Fibonacci. Antes de pensar que todas as complexidades dos montes de Fibonacci eram algumas idéias aleatórias, mas depois percebi que eram todas naturais e razoavelmente forçadas.

A. Rex
fonte
Obrigado! Esta pergunta estava em minha mente por um longo tempo. Na verdade, implementei o Dijkstra usando Fibonacci-Heaps antes de tentar Steiner-Trees. No entanto, parece que meus gráficos eram muito menos densos do que no seu exemplo. Eles tinham milhões de nós, mas um grau médio de apenas 5-6.
Mdm
O desempenho do Fib Heap é previsível através da sequência de operações. Eu escrevi um algoritmo pesado para Heap que acabou mais rápido com o Fib Heap (vs. Bin Heap). O truque foi agrupar o trabalho. Independentemente da frequência de qualquer operação, as mentiras diferença aqui: DecreaseKey - ExtractMin - DecreaseKey - ExtractMin contra DecreaseKey - DecreaseKey - ExtractMin - ExtractMin (cont abaixo.)
Gaminic
Este último será aproximadamente duas vezes mais rápido, porque o 2º ExtractMin é quase gratuito. Nosso algoritmo extrai um lote de elementos Min dos quais muitos são descartados; um desperdício em um Bin Heap, mas melhor em um Fib Heap. Infelizmente, isso não se reflete claramente na complexidade de tempo que as pessoas fornecem ao falar sobre algoritmos baseados em Heap. Com limites amortizados, a complexidade total não é simplesmente # operações * complexidade da operação.
Gaminic
1
Alguma chance de também tentar emparelhar pilhas e / ou pilhas relaxadas?
Thomas Ahle
1
Não sei por que seus resultados pareceram tão próximos, usei STL priority_queue vs heap de fibonacci auto-implementado e o heap binário ficou para trás por uma grande margem em meus testes .
Nicholas Pipitone
33

Knuth fez uma comparação entre a pilha de fibonacci e a pilha binária para árvores de abrangência mínima em 1993 para seu livro Stanford Graphbase . Ele descobriu que os fibonacci eram 30 a 60 precedentes mais lentos que os binários nos tamanhos de gráfico que ele estava testando, 128 vértices em densidades diferentes.

O código fonte está em C (ou melhor, CWEB, que é um cruzamento entre C, matemática e TeX) na seção MILES_SPAN.

cavalo de papel
fonte
1

aviso Legal

Eu sei que os resultados são bastante semelhantes e "parece que o tempo de execução é totalmente dominado por algo diferente da pilha" (@Alpedar). Mas não consegui encontrar nenhuma evidência disso no código. O código está aberto; portanto, se você encontrar algo que possa afetar o resultado do teste, informe-me.


Talvez eu tenha feito algo errado, mas escrevi um teste , com base na análise do A.Rex comparando:

  • Fibonacci-Heap
  • D-Ary-heap (4)
  • Pilha binária
  • Pilha descontraída

Os tempos de execução (apenas para gráficos completos) para todos os montões foram muito próximos. O teste foi realizado para gráficos completos com 1000,2000,3000,4000,5000,6000,7000 e 8000 vértices. Para cada teste, 50 gráficos aleatórios foram gerados e o resultado é o tempo médio de cada heap:

Desculpe a saída, não é muito detalhado, porque eu precisava criar alguns gráficos a partir de arquivos de texto.


Aqui estão os resultados (em segundos):

tabela de resultados da pilha

Guilherme Torres Castro
fonte
4
Quantas arestas existem em cada caso? E qual algoritmo você está executando exatamente? Seus resultados não fazem sentido se não soubermos com o que estamos lidando.
Kokx
Na verdade, todo o gráfico está completo, para que você possa calcular o número de arestas de cada caso. O que você quer dizer com "você está correndo exatamente". Eles estão na cabeceira das mesas.
Guilherme Torres Castro
22
Parece que o tempo de execução é totalmente dominado por algo diferente do heap (pode ser a geração de gráfico ou algum IO). Esses resultados quase exatamente iguais são inacreditáveis.
Alpedar
2
Bem, talvez o tempo seja dominado por outra coisa, mas tenho certeza de que não é o IO ou a geração dos gráficos. De qualquer forma, o código fonte está disponível e ficarei muito feliz se alguém encontrar um erro e corrigir a medida.
Guilherme Torres Castro
Esses testes parecem estar medindo algo completamente diferente. Você poderia comentar sobre o teste que executou? Lembre-se de que o Problema de Caminho Mais Curto em um gráfico completo é O (1) se as distâncias forem Geométricas / Euclidianas.
precisa saber é o seguinte
0

Também fiz um pequeno experimento com a pilha de Fibonacci. Aqui está o link para os detalhes: Experimentando com o dijkstras-algoritmo . Eu apenas pesquisei os termos "Fibonacci heap java" e tentei algumas implementações de código aberto existentes do hibrido Fibonacci. Parece que alguns deles têm algum problema de desempenho, mas há alguns que são bastante bons. Pelo menos, eles estão superando o desempenho PQ da pilha ingênua e binária no meu teste. Talvez eles possam ajudar a implementar o eficiente.

gabormakrai
fonte