グラフがあり、その直径(2つのノード間の最大距離)を見つける必要がある場合、O(log v * (v + e))
の複雑さでどのようにそれを行うことができますか。
ウィキペディアによると、これは Dijkstra's algorithm
binary heap
。しかし、これがどのように機能するのかわかりません。誰か説明してもらえますか?
または、擬似コードを表示しますか?
一般的なグラフG=(V,E)
の場合、直径の計算で知られているO(log V * (V + E))
時間計算量アルゴリズムはありません。現在の最良の解決策は、O(V*V*V)
です。たとえば、FloydWarshallのアルゴリズムを使用してすべての最短経路を計算します。スパースグラフの場合、つまりE
がo(N*N)
にある場合、JohnsonのアルゴリズムはO(V*V*log(V)+V*E)
の時間計算量を向上させます。
グラフに非巡回(ツリー)などの特定のプロパティがある場合は、改善できます。
悪いニュースは、一般的なケースではダイクストラでは不十分だということです...
私はスレッドに1年遅れていることを知っていますが、これらの答えのどれも正しいとは思いません。 OPはコメントで、エッジは重み付けされていないと述べています。この場合、最良のアルゴリズムは$ O(n ^ {\ omega})\ log n $時間で実行されます(ここで、$\omega $は行列乗算の指数であり、現在、VirginiaWilliamsによって上限は$ 2.373 $です)。
このアルゴリズムは、重み付けされていないグラフの次のプロパティを利用します。 $ A $を、ノードごとに自己ループが追加されたグラフの隣接行列とします。その場合、$(A ^ k)_ {ij} $は、$ d(i、j)\ le k $の場合、ゼロ以外になります。この事実を使用して、$ A ^ k $の$\log n $値を計算することにより、グラフの直径を見つけることができます。
アルゴリズムの仕組みは次のとおりです。$ A $をグラフの隣接行列とし、ノードごとに自己ループを追加します。 $ M_0 = A $を設定します。 $ M_k $には少なくとも1つのゼロが含まれていますが、$ M_ {k + 1} = M_ {k} ^ 2 $を計算します。
最終的に、すべてゼロ以外のエントリを持つ行列$ M_ {K} $が見つかります。上記のプロパティによる$ K\le\log n $はわかっています。したがって、これまでのところ、行列の乗算は$ O(\ log n)$回しか実行していません。これで、$ M_ {K} = A ^ {2 ^ K} $と$ M_ {K-1} = A ^ {2 ^ {K-1}} $の間で二分探索を続けることができます。 $ B = M_ {K-1} $を次のように設定します。
DIAMETER = $ 2 ^ {k-1} $に設定します。 $ i =(K-2\dots 0)$の場合、次のテストを実行します。
$ B $に$ M_ {i} $を掛けて、結果の行列にゼロがないか確認します。ゼロが見つかった場合は、この行列積に$ B $を設定し、DIAMETERに$ 2 ^ i $を追加します。それ以外の場合は、何もしません。
最後に、DIAMETERを返します。
実装の詳細として、各行列の乗算が実行された後、行列内のすべての非ゼロエントリを$ 1 $に設定する必要がある場合があります。これにより、値が大きくなりすぎて扱いにくくなり、短時間で書き留めることができなくなります。
Eciが述べたように、解決策の1つは、Floyd-Warshallアルゴリズムを使用することです。そのためのコードが必要な場合は、C++バージョンを見つけることができます ここ 。
Boost BGLには、「rcm_queue」という名前の小さな拡張dequeクラスがあります。これを使用すると、単純な幅優先探索で頂点の離心率を見つけることができます。これは、O(E)の複雑さを意味します。
http://www.boost.org/doc/libs/1_54_0/boost/graph/detail/sparse_ordering.hpp
直径はすべての頂点の離心率を調べることで計算できるため、複雑度がO(V * E)のグラフの直径を計算できます。
特に、deg(G)<<< Vの非常にまばらなグラフの場合、実行時間の良いものは見つかりませんでした。
私はフロイドウォーシャルアルゴリズムを調べませんでした。私はちょうど5.000.000を超える頂点を持つグラフを扱っていましたが、15未満の頂点の最高次数で、これはおそらくV * V * log(V)の複雑さのアルゴリズムよりも優れているはずです。
[〜#〜]編集[〜#〜]
確かに、これは無向グラフでのみ機能し、負の重みはありません(または、重みがないだけですか?atmはわかりません)
グラフの説明-無向および無加重、nノード、mエッジグラフの直径については、ノードのすべてのペア間の最短経路を計算する必要があります。ソースノードから他のすべてのノードへの最短パスは、無向および無加重グラフのBFSアルゴリズムを使用して計算できます。時間計算量はO(n + m) 1ノードの場合です。ここでは、n個のノードに対してBFSを実行する必要があるため、ノードのすべてのペア間の最短パスを見つけるための合計時間計算量はO(n(n + m))になります。 n(n-1)/ 2ノードのペアがあるため、すべてのノード間の最短パスの値はn(n-1)/ 2になります。直径については、これらの値の最大値を取得する必要があります。これもO(n * n)です。したがって、最終的な時間計算量は次のようになります。
O(n(n + m)) + O(n*n) = O(n(2n + m)) = O(n(n + m))
ノードのすべてのペア間の最短パスを取得するための擬似コード。 Shortest_pathsという名前の行列を使用して、ノードの任意の2つのペア間の最短パスを格納しています。頂点にアクセスしたかどうかを示すtrueまたはfalseの値を持つflagという名前の辞書を使用しています。 BFSの反復ごとに、この辞書をすべてfalseに初期化します。キューQを使用してBFSを実行します。アルゴリズムBFS(すべてのノード用)
Initialize a matrix named Shortest_paths[n][n] to all -1.
For each source vertex s
For each vertex v
Flag[v] = false
Q = empty Queue
Flag[s] = true
enQueue(Q,s)
Shortest_paths[s][s] = 0
While Queue is not empty
Do v = Dequeue(Q)
For each w adjacent to v
Do if flag[w] = false
Flag[w] = true
Shortest_paths[s][w] = Shortest_paths[s][v] + 1
enQueue(Q,w)
Diameter = max(Shortest_paths)
Return Diameter
実際、グラフが非常に大きい場合は、最短距離を見つけるためにダイクストラのアルゴリズムを使用する必要があります。したがって、OPのグラフにあるノードの数によって異なります。
まず、 グラフの凸包 を見つける必要があります(O(nh)であることがわかります。ここで、hは船体上のノードの数です)。直径の点は凸包上にあるため、問題はh点の中で最も遠い点を見つけることになります。したがって、全順序はO(nh + h * h)になります。
グラフが重み付けされていないと仮定すると、次の手順でO(V *(V + E))の解を見つけることができます。ここで、Vは頂点の数、Eはエッジの数です。
2つの頂点u、v間の距離を、uとvの間の最短経路の長さとして定義しましょう。d(u、v)で表します。頂点vの離心率をe(v) =と定義します。 max {d(u、v)| u∈V(G)}(V(G)はグラフGの頂点のセットです)Diameter(G)= max {e(v)|を定義します。 v∈V(G)}
次にアルゴリズムについて説明します。
1- V(G)の各頂点vについて、BFS(v)を実行し、各頂点から他の頂点までの距離の2次元配列を作成します。 (各頂点から他の頂点までの距離の計算は、 BFSアルゴリズム で簡単に実行できます)
2-ステップ1で作成した2次元配列から各頂点のe(v)を計算します3-ステップ2から最大のe(v)を見つけて直径(G)を計算します
このアルゴリズムを分析すると、O(V *(V + E))の最初のステップが支配的であることが簡単にわかります。
線形時間で実行される別のアルゴリズムO(V + E)1-任意のランダムな頂点でBFSを実行v∈V(G)2-最大距離で頂点uを選択3-その頂点でBFSを再度実行するu 4-直径は、ステップ3から生成される最大距離です。