ダイクストラのアルゴリズムとプリムのアルゴリズムの正確な違いは何ですか?私はプリムがMSTを与えることを知っていますが、ダイクストラによって生成されたツリーもMSTになります。次に、正確な違いは何ですか?
graph[u][v] < key[v]
とDijkstra dist[u]+graph[u][v] < dist[v]
です。したがって、これら2つのページのグラフからわかるように、主に 2行のコードが原因で、グラフは異なります。
ダイクストラのアルゴリズムとプリムのアルゴリズムの正確な違いは何ですか?私はプリムがMSTを与えることを知っていますが、ダイクストラによって生成されたツリーもMSTになります。次に、正確な違いは何ですか?
graph[u][v] < key[v]
とDijkstra dist[u]+graph[u][v] < dist[v]
です。したがって、これら2つのページのグラフからわかるように、主に 2行のコードが原因で、グラフは異なります。
回答:
Primのアルゴリズムは、グラフの最小スパニングツリーを構築します。これは、グラフ内のすべてのノードを接続するツリーであり、すべてのノードを接続するすべてのツリーの中で総コストが最小になります。ただし、MSTの2つのノード間のパスの長さは、元のグラフの2つのノード間の最短パスではない場合があります。たとえば、MSTは、グラフのノードを物理的に配線して、最小の総コストでノードに電力を供給したい場合に役立ちます。2つのノード間のパスの長さが最適でなくてもかまいません。重要なのは、ノードが接続されているという事実だけだからです。
ダイクストラのアルゴリズムは、あるソースノードから始まる最短パスツリーを構築します。最短パスツリーは、グラフ内のすべてのノードをソースノードに接続するツリーであり、ソースノードからグラフ内の他のノードまでのパスの長さが最小になるというプロパティがあります。これは、たとえば、すべての人が主要な重要なランドマークに到達するのを可能な限り効率的にする道路網を構築したい場合に役立ちます。ただし、最短パスツリーは最小スパニングツリーであるとは限りません。最短パスツリーのエッジのコストの合計は、MSTのコストよりもはるかに大きくなる可能性があります。
もう1つの重要な違いは、アルゴリズムが機能するグラフのタイプに関するものです。MSTの概念はグラフが本質的に無向であると想定しているため、Primのアルゴリズムは無向グラフでのみ機能します。(有向グラフには「最小全域樹」と呼ばれるものがありますが、それらを見つけるアルゴリズムははるかに複雑です)。ダイクストラのアルゴリズムは、最短パスツリーを実際に有向にすることができるので、有向グラフで正常に動作します。さらに、ダイクストラのアルゴリズムは、負のエッジの重みを含むグラフで必ずしも正しい解をもたらすわけではありませんが、プリムのアルゴリズムはこれを処理できます。
お役に立てれば!
the length of a path between **any** two nodes
、srcノードとプリムの他のノードの間の距離が最短でなければ最短ではない理由に焦点を当てる必要があります。私は彼がPrimのsrcノードに他のノードを要求しているに違いないと思います。なぜプリムの2つのノードについて話しましたか?もちろん、それは最短ではありません。
ダイクストラのアルゴリズムはMSTを作成せず、最短経路を見つけます。
このグラフを考えてみましょう
5 5
s *-----*-----* t
\ /
-------
9
最短パスは9ですが、MSTは10の別の「パス」です。
The shortest path is 9
からsへ。ダイクストラのアルゴリズムによって生成された、sから始まるグラフの重みは14(5 + 9)です。
PrimアルゴリズムとDijkstraアルゴリズムは、「relax関数」を除いてほとんど同じです。
堅苦しい:
MST-PRIM (G, w, r) {
for each key ∈ G.V
u.key = ∞
u.parent = NIL
r.key = 0
Q = G.V
while (Q ≠ ø)
u = Extract-Min(Q)
for each v ∈ G.Adj[u]
if (v ∈ Q)
alt = w(u,v) <== relax function, Pay attention here
if alt < v.key
v.parent = u
v.key = alt
}
ダイクストラ:
Dijkstra (G, w, r) {
for each key ∈ G.V
u.key = ∞
u.parent = NIL
r.key = 0
Q = G.V
while (Q ≠ ø)
u = Extract-Min(Q)
for each v ∈ G.Adj[u]
if (v ∈ Q)
alt = w(u,v) + u.key <== relax function, Pay attention here
if alt < v.key
v.parent = u
v.key = alt
}
唯一の違いは、リラックス機能である矢印で示されています。
alt = w(u,v)
alt = w(u,v) + u.key
edges()
MSTエッジを返すメソッドがありますが、DijkstraにはdistanceTo(v)
、pathTo(v)
ソースから頂点vへの距離とソースから頂点vへのパスをそれぞれ返す、があります。ここで、sは、ダイクストラを初期化する頂点です。
edges()
異なるsで初期化ダイクストラはdistanceTo(v)
、の異なる出力を返しますpathTo(v)
。
Dijsktraのアルゴリズムはノードiからすべてのノードまでの最小距離を見つけます(iを指定)ます。したがって、代わりにノードiから最小距離ツリーを取得します。
プリムアルゴリズムは、特定のグラフの最小スパニングツリーを取得します。すべてのコストの合計が可能な限り最小であるときに、すべてのノードを接続するツリー。
したがって、ダイクストラを使用すると、選択したノードから他のノードに最小コストで移動できます。プリムではこれを取得できません
唯一の違いは、Primのアルゴリズムは最小コストエッジを格納するのに対し、Dijkstraのアルゴリズムはソース頂点から現在の頂点までの合計コストを格納することです。
ダイクストラは、コストが最小になるように、ソースノードから宛先ノードへの道を提供します。ただし、プリムのアルゴリズムは、すべてのノードが接続され、総コストが最小になるような最小のスパニングツリーを提供します。
簡単な言葉で:
したがって、複数の都市を結ぶ列車を配備する場合は、プリムのアルゴを使用します。しかし、ある都市から別の都市に移動して時間をできるだけ節約したい場合は、ダイクストラのアルゴリズムを使用します。
どちらも、次のようにまったく同じ汎用アルゴリズムを使用して実装できます。
Inputs:
G: Graph
s: Starting vertex (any for Prim, source for Dijkstra)
f: a function that takes vertices u and v, returns a number
Generic(G, s, f)
Q = Enqueue all V with key = infinity, parent = null
s.key = 0
While Q is not empty
u = dequeue Q
For each v in adj(u)
if v is in Q and v.key > f(u,v)
v.key = f(u,v)
v.parent = u
プリムの場合はpass f = w(u, v)
、ダイクストラの場合はpass f = u.key + w(u, v)
です。
もう1つの興味深い点は、上記のGenericは幅優先検索(BFS)も実装できることですが、高価な優先キューは実際には必要ないため、やりすぎです。上記の汎用アルゴリズムをBFSに変換するには、次を渡します。f = u.key + 1
するにはすべての重みを1に強制するのと同じをます(つまり、BFSは、ポイントAからBに移動するために必要なエッジの最小数を提供します)。
直感
上記の一般的なアルゴリズムについて考える良い方法の1つは次のとおりです。まず、2つのバケットAとBから始めます。最初に、すべての頂点をBに配置して、バケットAを空にします。次に、1つの頂点をBからAに移動します。次に、Aの頂点からBの頂点に交差するすべてのエッジを確認します。これらの交差エッジからいくつかの基準を使用して1つのエッジを選択し、対応する頂点をBからA. Bが空になるまで、このプロセスを繰り返します。
このアイデアを実装する強力な方法は、Bに渡るAの頂点のエッジの優先度キューを維持することです。明らかに、グラフがスパースでない場合、これは面倒です。では問題は、代わりに頂点の優先キューを維持できるでしょうか?実際、これは最終的にBからどの頂点を選択するかを決定することで可能になります。
歴史的背景
興味深いのは、両方のアルゴリズムの背後にある手法の一般的なバージョンが、電子コンピューターがなかったとしても、概念的には1930年と同じくらい古いことです。
この物語は、家族の友人がモラビアの国(現在のチェコ共和国の一部)の都市を最小限のコストの電線で接続する方法を理解するためのアルゴリズムを必要としたOtakarBorůvkaから始まります。彼のアルゴリズムは、1926年に数学関連のジャーナルに発表されました。当時、コンピューターサイエンスは存在しなかったためです。これは、ボリフカのアルゴリズムの改善を考えて1930年に公開したヴォイチェフジャーニークに注目されました。彼は、実際には、1957年に再発見したPrimのアルゴリズムと同じアルゴリズムを発見しました。
これらすべてとは独立して、1956年にダイクストラは彼の研究所が開発した新しいコンピューターの機能を実証するプログラムを書く必要がありました。彼はコンピューターでオランダの2つの都市間を移動するための接続を見つけることができればすばらしいと彼は思った。彼はアルゴリズムを20分で設計しました。彼は64の都市のグラフを作成しました(彼のコンピューターは6ビットであったため)単純化し、この1956年のコンピューターのコードを書きました。しかし、主にコンピューターサイエンスジャーナルが存在しないため、彼はアルゴリズムを公開しませんでした。これはあまり重要ではないと考えました。翌年、彼は新しいコンピューターの端子を接続して、ワイヤーの長さを最小限に抑える問題について学びました。彼はこの問題について考え、Jarník/ Prim 'を再発見しました 1年前に彼が発見した最短経路アルゴリズムと同じ手法を再び使用するアルゴリズム。彼彼のアルゴリズムは両方ともペンや紙を使わずに設計されたと述べた。1959年に、彼は両方のアルゴリズムを2ページ半の長さの論文で発表しました。
ダイクストラのアルゴリズムのウィキペディアの記事から直接:
ダイクストラのアルゴリズムの基礎となるプロセスは、プリムのアルゴリズムで使用される貪欲なプロセスに似ています。プリムの目的は、グラフ内のすべてのノードを接続する最小のスパニングツリーを見つけることです。ダイクストラは2つのノードのみに関係しています。プリムは、開始ノードからのパスの合計重みを評価せず、個々のパスのみを評価します。
最近同じ質問に悩まされて、私の理解を共有したいと思います...
これら2つのアルゴリズム(ダイクストラとプリム)の根本的な違いは、それらが解決するように設計された問題、つまり2つのノード間の最短パスと最小スパニングツリー(MST)の根本だと思います。形式は、たとえばノードsとt の間の最短経路を見つけることであり、合理的な要件は、グラフの各エッジに最大で1回アクセスすることです。ただし、すべてのノードにアクセスする必要はありません。後者(MST)は、すべてのノードに(最大で1回)アクセスするようにするものであり、各エッジに最大で1回アクセスするという同じ合理的な要件があります。
ことではダイクストラは長い間、私はから得ることができる「テイクショートカット」に私たちを可能にする、と述べたのにトンの結果を気にせず、 -私が得ればトン、私が行っています!MSTにはsからtへのパスもありますが、このs - tパスは残りのすべてのノードを考慮して作成されるため、このパスは、ダイストラのアルゴリズムで検出されるs - tパスより長くなる可能性があります。以下は、3つのノードを持つ簡単な例です。
2 2
(s) o ----- o ----- o (t)
| |
-----------------
3
上端のそれぞれのコストが2で、下端のコストが3であるとしましょう。中間ノードは関係ないので、ダイクトラは下のパスを取るように指示します。一方、Primは、上部の2つのエッジを持つMSTを返し、下部のエッジを破棄します。
このような違いは、実装の微妙な違いにも反映されています。ダイクストラのアルゴリズムでは、新しいノードを吸収した後、sからの最短パスを更新するために(ノードごとに)ブックキーピングステップが必要ですが、Primのアルゴリズムではそのような必要はありません。
基本的なアルゴリズムの主な違いは、異なるエッジ選択基準にあります。一般に、どちらも次のノードを選択するために優先キューを使用しますが、現在の処理ノードの隣接ノードを選択するための異なる基準があります。
def dijkstra(g, s):
q <- make_priority_queue(VERTEX.distance)
for each vertex v in g.vertex:
v.distance <- infinite
v.predecessor ~> nil
q.add(v)
s.distance <- 0
while not q.is_empty:
u <- q.extract_min()
for each adjacent vertex v of u:
...
def prim(g, s):
q <- make_priority_queue(VERTEX.distance)
for each vertex v in g.vertex:
v.distance <- infinite
v.predecessor ~> nil
q.add(v)
s.distance <- 0
while not q.is_empty:
u <- q.extract_min()
for each adjacent vertex v of u:
if v in q and weight(u, v) < v.distance:// <-------selection--------
...
vertex.distanceの計算は2番目の異なる点です。
ダイクストラのアルゴリズムはノードiとjの間の単一ソースの最短経路問題ですが、プリムのアルゴリズムは最小のスパニングツリー問題です。これらのアルゴリズムは、「貪欲なアルゴリズム」というプログラミング概念を使用しています
これらの概念を確認する場合は、にアクセスしてください。
ダイクストラアルゴリズムは、最短経路を見つけるためにのみ使用されます。
では最小全域木(プリムのか、クラスカル法)あなたは、最小エッジ値と最小egdesを取得します。
例:-多数のワイヤーを必要とする巨大なネットワークを作成したくない状況を考えてください。ワイヤーのこれらのカウントは、最小スパニングツリー(プリムまたはクラスカルのアルゴリズム) を使用して実行できます(つまり、最小限のコストで巨大な有線ネットワーク接続を作成するための最小限の数のワイヤーを提供します。
一方、「ダイクストラアルゴリズム」は、ノードを相互に接続しながら2つのノード間の最短パスを取得するために使用されます。
最も簡単な説明は、プリムでは開始ノードを指定しないことですが、ダイクストラでは(開始ノードが必要)、指定されたノードから他のすべてのノードへの最短パスを見つける必要があります。
@templatetypedefは、MSTと最短パスの違いをカバーしています。アルゴリズムの違いについては別の記事で説明しましたので、入力としてもう1つのパラメーターをとる同じ汎用アルゴリズムを使用して両方を実装できることを示して答えてください:function f(u,v)
。PrimとDijkstraのアルゴリズムの違いは、単にどちらf(u,v)
を使用するかです。
コードレベルでは、もう1つの違いはAPIです。
Primはソース頂点sで初期化しPrim.new(s)
ます。つまり、sは任意の頂点にすることができ、sに関係なく、最小スパニングツリー(MST)のエッジである最終結果は同じです。MSTエッジを取得するには、メソッドを呼び出しますedges()
。
ダイクストラをソース頂点sで初期化Dijkstra.new(s)
します。つまり、他のすべての頂点への最短パス/距離を取得します。最終結果は、sから他のすべての頂点への最短パス/距離です。sによって異なります。sから任意の頂点vまでの最短パス/距離を取得するには、メソッドdistanceTo(v)
とをpathTo(v)
それぞれ呼び出します。