web-dev-qa-db-ja.com

誰かが実際にフィボナッチヒープを効率的に実装しましたか?

あなたの誰かが Fibonacci-Heap を実装したことがありますか?私は数年前にそうしましたが、アレイベースのBinHeapsを使用するよりも数桁遅かったです。

当時、私はそれが研究が必ずしもそれが主張するほど良いとは限らない方法の貴重な教訓と考えました。ただし、多くの研究論文は、フィボナッチヒープの使用に基づいてアルゴリズムの実行時間を主張しています。

効率的な実装を実現できましたか?または、フィボナッチヒープがより効率的であるほど大きいデータセットを使用しましたか?もしそうなら、いくつかの詳細をいただければ幸いです。

148
mdm

Boost C++ライブラリ には、フィボナッチヒープの実装がboost/pending/fibonacci_heap.hpp。このファイルは明らかにpending/何年もの間、私の予想では決して受け入れられません。また、その実装にはバグがありましたが、それは私の知り合いであり、万能のクールなアーロンウィンザーによって修正されました。残念ながら、オンラインで見つけることができるそのファイルのほとんどのバージョン(およびUbuntuのlibboost-devパッケージのバージョン)にはまだバグがありました。 Subversionリポジトリから a clean version をプルする必要がありました。

バージョン 1.49Boost C++ライブラリ 以降、フィボナッチヒープを含む多くの新しいヒープ構造体が追加されました。

dijkstra_heap_performance.cppdijkstra_shortest_paths.hpp の修正バージョンに対してコンパイルして、フィボナッチヒープとバイナリヒープを比較できました。 (行typedef relaxed_heap<Vertex, IndirectCmp, IndexMap> MutableQueuerelaxedfibonacciに変更します。)最初に、最適化でコンパイルするのを忘れました。その場合、フィボナッチヒープとバイナリヒープはほぼ同じパフォーマンスを発揮します。非常に強力な最適化でコンパイルした後、バイナリヒープが大幅に向上しました。私のテストでは、フィボナッチヒープは、グラフが非常に大きく高密度である場合にのみバイナリヒープよりも優れていました。

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.

私の知る限り、これはフィボナッチヒープとバイナリヒープの基本的な違いに触れています。 2つのデータ構造の唯一の本当の理論的な違いは、フィボナッチヒープが(償却)一定時間で減少キーをサポートすることです。一方、バイナリヒープは、配列としての実装から大きなパフォーマンスを得ます。明示的なポインター構造を使用すると、フィボナッチヒープのパフォーマンスが大幅に低下します。

したがって、フィボナッチヒープを実際に使用するには、deduce_keysが非常に頻繁に発生するアプリケーションで使用する必要があります。ダイクストラに関して言えば、これは基礎となるグラフが密であることを意味します。いくつかのアプリケーションは本質的に減少する可能性があります。 Nagomochi-Ibaraki minimum-cut algorithm を試してみたかったのは、明らかに多くのreduce_keysが生成されるためですが、タイミング比較を機能させるのは大変でした。

警告:何か間違ったことをした可能性があります。これらの結果を自分で再現してみてください。

理論上の注意:reduce_keyでのフィボナッチヒープのパフォーマンスの向上は、ダイクストラのランタイムなどの理論的なアプリケーションにとって重要です。フィボナッチヒープは、挿入およびマージの際にバイナリヒープよりも優れています(フィボナッチヒープの両方とも一定時間で償却されます)。挿入は、Dijkstraのランタイムに影響を与えないため、本質的には無関係です。また、バイナリヒープを変更して、償却された一定時間内に挿入することは非常に簡単です。一定時間でのマージは素晴らしいですが、このアプリケーションには関係ありません。

個人ノート:私と友人は、フィボナッチヒープの(理論上の)実行時間を複製しようとする新しい優先度キューについて説明する論文を書いたことがあります複雑さなし。論文は決して公開されませんでしたが、私の共著者はバイナリヒープ、フィボナッチヒープ、および独自の優先度キューを実装して、データ構造を比較しました。実験結果のグラフは、フィボナッチヒープの合計比較がバイナリヒープをわずかに上回っていることを示しており、比較コストがオーバーヘッドを上回る状況でフィボナッチヒープのパフォーマンスが向上することを示唆しています。残念ながら、私はコードを入手できず、おそらくあなたの状況では比較は安価であるため、これらのコメントは関連していますが、直接適用できません。

ちなみに、フィボナッチヒープのランタイムを独自のデータ構造と一致させることを強くお勧めします。フィボナッチヒープを単純に再発明したことがわかりました。フィボナッチヒープの複雑さはすべてランダムなアイデアであると考える前に、その後、それらはすべて自然でかなり強制されていることに気付きました。

133
A. Rex

Knuthは、彼の著書 Stanford Graphbase について、1993年に最小スパニングツリーのフィボナッチヒープとバイナリヒープを比較しました。彼は、フィボナッチが、テストしているグラフサイズでのバイナリヒープよりも30〜60%遅く、密度が異なる128個の頂点であることを発見しました。

ソースコード は、セクションMILES_SPANのC(またはC、math、TeXのクロスであるCWEB)にあります。

32
paperhorse

免責事項

結果は非常によく似ており、「実行時間はヒープ以外のものに完全に支配されているように見える」ことを知っています(@Alpedar)。しかし、コードにはその証拠が見つかりませんでした。コードは開いているので、テストの結果に影響を与える可能性のあるものを見つけられたら、教えてください。


私は何か間違ったことをしたかもしれませんが、私は A.Rex anwser比較に基づいて テストを書いた です:

  • フィボナッチヒープ
  • D-Ary-heap(4)
  • バイナリヒープ
  • リラックスヒープ

すべてのヒープの実行時間(完全なグラフのみ)は非常に近かった。テストは、頂点が1000、2000、3000、4000、5000、6000、7000、および8000の完全なグラフに対して行われました。各テストで50個のランダムグラフが生成され、出力は各ヒープの平均時間です。

出力については申し訳ありませんが、テキストファイルからグラフを作成するために必要だったため、あまり冗長ではありません。


結果は次のとおりです(秒単位)。

heap result table

また、フィボナッチヒープで小さな実験を行いました。詳細のリンクは次のとおりです。 Experimenting-with-dijkstras-algorithm 。 「フィボナッチヒープJava」という用語を検索して、フィボナッチヒープの既存のオープンソース実装をいくつか試しました。それらのいくつかにはパフォーマンスの問題があるようですが、かなり良いものもあります。少なくとも、彼らは私のテストで素朴でバイナリヒープのPQパフォーマンスを上回っています。たぶん彼らは効率的なものを実装するのを助けることができます。

0
gabormakrai