HopcroftとKarp -Wikipediaのアルゴリズム
HopcroftとKarpのアルゴリズム (1973年にJohn E. HopcroftとRichard M. Karpによって開発されました)は、グラフ理論で使用され、二部の最大のカーディナリティとのマッチングを決定します。それはマッチングから抜け出しますが、これにはエッジが含まれていません。また、まだ対応のない結び目の間に交互のパスを構築します。そのようなパスはそれぞれ、1つのエッジの周りに一致する拡大(増強)を提供します。
エッジのエッジを持つグラフです
マッチング
与えられると、木であるコヒーレントサブグラフ、つまりサイクルが含まれておらず、存在することを検討してください
- 未払いの結び目として 根 、
- ツリー内の根から到達できるペアの結び目 代わりの ストレートエッジのパス。 交互 パスのエッジも交互に行うことを意味します 属し、それほどではありません 属する。したがって、これらのノットはルートからまっすぐです。
- 交互のパスに沿ったすべてのノットとエッジ。これには、ルートから奇妙な距離を持つノードも含まれます。
一般的なノードを持っていないそのような木の量は呼び出されます 森 。
結び目
と
森の2つの異なる木から、それぞれが根からまっすぐに、端を通ります
このエッジを接続できない場合
ノットは、とにかく対応していないルートでない限り、別の端でツリー内ですでにペアになっているためです。エッジの端のあるパス
1つの木の根元から
他のツリーのルートには、対応のないスタートとエンドノードのある交互のパスがあります。そのような道はなります
– パスを獲得します なぜなら、なぜなら
エッジを含むマッチングです
。
逆に、マッチングが適用されます
それにはより多くのエッジが含まれています
、エッジの量のサブグラフ
その間のすべてのパスが結果です
と
代替、そして少なくとも
道
– 自動化は相互ノードなしでなければなりません。
したがって、何もない場合、それは最大のマッチングです
– パスを獲得します。
ハンガリーの森 [ 編集 | ソーステキストを編集します ]
検討中の森林を定義するとき、二部グラフがあるとはまだ想定されていません。二部グラフで
しかし、さらに適用されます:そこには、のルートから直接距離があります
また
、ルートがどこにあるかに応じて。森に結び目がない場合
と
と
最後のセクションのように、森林は、言及されている特性に準拠して拡大することができなくなりましたが、それは1つと呼ばれています ハンガリーの森 。二極性のため、ハンガリーの森があるときにマッチングが最大のマッチングであることを示すことができます。
アルゴリズム [ 編集 | ソーステキストを編集します ]
次のアルゴリズムは、HopcroftとKarpのアルゴリズムの予備段階です。彼は一致して二部グラフを構築します
言及されたプロパティのある森、それ
- 森から始めます。森は、すべての対応のないノードを根として含みますが、エッジはありません。
- 結び目の端を見つけます その根から結び目まで直接距離のある森の 森に属していないか、その根から直接距離を持っている人。そのような結び目がなければ、森はハンガリーの森です。アルゴリズムを終了します。
- 滝 その根からコインには、まっすぐな長さのナットの道があります。 後 。与える – のパスを獲得します その上 後 アルゴリズムを後ろにして終了します。
- 滝 森の一部ではありません ペアリング、約 :ノットを追加します と エッジと同様に と 森に追加して、ステップ2に戻ります。
最初にアルゴリズム
実行された。彼がステップ3にいる場合
– パスを獲得します
終わり、意志
終えた
交換すると、アルゴリズムが再度実行されました。アルゴリズムがステップ2でハンガリーの森で終わる場合、次に
最新の後、最大のマッチングです
他のケースでは、一致が2ノット増加するため、アルゴリズム全体が発生します。アルゴリズムの1回の実装を使用したランタイムは、エッジの数に比例します
、実装が繰り返される合計項は、エッジとノットの数の積に比例します。
この例の二部グラフには、10ノットと10のエッジがあります。左側では、すべてのノットが最初のフェーズの前に無料で、マッチングは空です。すべての拡張パスは、の結び目の間の単一の端にあります
と結び目
削減。たとえば、幅の検索でエッジが選択されます。
結び目ごとに
自由な結び目
最小のインデックスで選択されています。この量のパスは最大であり、すべて同じ長さ1、保存されたマッチングにはサイズ4、2つのフリーノットが残り、2つのフリーノットが残ります。
と
。
2番目のフェーズは、
またはの唯一のフリーノードから
見つけるには。指定されたパス
マッチング内のマッチングエッジと赤いエッジの外側の黒いエッジを切り替えます。彼は長さ5の長さです。長さ3のパスはないことがわかりますが、長さ7のパスがあります。
。長さ5のパスとの以前のマッチングの対称的な違いに起因するマッチングは、サイズ5の例になります。これは、自由ノードがもうないために最大です。
アルゴリズムの合計期間は、いくつかの場合に減らすことができます
– パスを同時に表示できます。そうです
最短の長さ
– パスを獲得します。私たちは見ます
-Augmenting Knotty Junks Paths
長さ
もう誰も
– 長さの経路
追加できます。その後、それを示すことができます
- 最大マッチングと 、したがって、拡張アルゴリズムが配信されます マッチングを実行します と と うなずき – 少なくとも長さのパス は。誰もいないからです ノットは、このパスの2つに含まれています 最大マッチングがさらに後に行われなければならないように ランを通して。したがって、HopcroftとKarpからのアルゴリズムの合計項は、ノードのエッジ数と平方根の数からの製品に比例します。
薄いグラフの場合、HopcroftとKarpのアルゴリズムは、最高の最悪の用語を持ち続けています。濃いグラフの場合、新しいアルゴリズムはわずかに優れた用語を実現します。 [初め]
このアルゴリズムは、Goldberg-Tarjanアルゴリズムの使用に基づいています。このアルゴリズムによって生成されたマッチングがほぼ最適な場合、HopcroftとKarpのアルゴリズムが切り替えられます。数人の著者が、二部マッチングのためのアルゴリズムの実験的比較を実施しています。彼らの結果は、HopcroftとKarpのアルゴリズムが理論ほど実際には良くないことを示している傾向があります。それは、パスを増強するための検索のためのより単純な戦略と、プッシュリラベルテクニックの両方によって上回られています。 [2]
プログラミング言語C ++の次の例は、二部グラフのHopcroftとKarpからのアルゴリズムの実装を示しています。二部グラフはクラスです bipartitegraph 宣言されています。メソッド HOPCROFTKARP 使用幅検索とディープ検索。プログラムを実行するとき、メソッドは次のとおりです 主要 使用して、コンソールにマークされたノードのリストを出力します。 [3]
#含む
#含む 使用 名前空間 std ; #define nil 0 #define inf int_max //グラフのノードのデータ型を宣言します struct ノード { int 索引 ; string value; Node* next; }; // Deklariert die Klasse für den bipartiten Graphen class BipartiteGraph { int count1, count2; // Anzahl der Knoten auf der linken und rechten Seite des bipartiten Graphen list<Node>* adjacencyList; // Anzahl der Knoten auf der linken und rechten Seite des bipartiten Graphen Node* pair1, * pair2; int distance[]; // Basiszeiger, die in der Methode HopcroftKarp verwendet werden // Deklariert die Interfaces für die Methoden public: BipartiteGraph(int count1, int count2); // Konstruktor void AddEdge(Node* startNode, Node* targetNode); bool BreadthFirstSearch(); bool DepthFirstSearch(Node* startNode); int HopcroftKarp(); }; // Gibt die Matchingzahl des maximalen Matching zurück int BipartiteGraph::HopcroftKarp() { pair1 = new Node[count1 + 1]; // Speichert ein Paar von Knoten, wobei pair1[u] eine Ecke auf der linken Seite des bipartiten Graphen ist. Wenn nicht vorhanden, wird der Nullzeiger NIL gespeichert. pair2 = new Node[count2 + 1]; // Speichert ein Paar von Knoten, wobei pair1[v] eine Ecke auf der rechten Seite des bipartiten Graphen ist. Wenn nicht vorhanden, wird der Nullzeiger NIL gespeichert. // Initialisiert die Paare mit dem Nullzeiger NIL for (int i = 0; i <= count1; i++) { (&pair1)[i] = NIL; } for (int i = 0; i <= count2; i++) { (&pair2)[i] = NIL; } int result = 0; // Initialisierung while (BreadthFirstSearch()) { for (int i = 1; i <= count1; i++) { Node* node = &pair1[i]; if (node == NIL && DepthFirstSearch(node)) { result++; } } } return result; } // Gibt true zurück, wenn es einen augmentierenden Pfad gibt, sonst false bool BipartiteGraph::BreadthFirstSearch() { queue<Node> nodeQueue; for (int i = 1; i <= count1; i++) { Node* node = &pair1[i]; if (node == NIL) { distance[i] = 0; nodeQueue.push(*node); } else { distance[i] = INF; } } distance[NIL] = INF; while (!nodeQueue.empty()) { int currentIndex = nodeQueue.front().index; nodeQueue.pop(); if (distance[currentIndex] < distance[NIL]) { list<Node>::iterator i; for (i = adjacencyList[currentIndex].begin(); i != adjacencyList[currentIndex].end(); ++i) { Node* node = &pair2[(*i).index]; int index = (*node).index; if (distance[index] == INF) { distance[index] = distance[currentIndex] + 1; nodeQueue.push(*node); } } } } return (distance[NIL] != INF); } // Gibt true zurück, wenn es einen augmentierenden Pfad mit startNode gibt, sonst false bool BipartiteGraph::DepthFirstSearch(Node* startNode) { int startIndex = startNode->index; // if (startNode != NIL) { list<Node>::iterator i; for (i = adjacencyList[startIndex].begin(); i != adjacencyList[startIndex].end(); ++i) { Node j = *i; if (distance[pair2[startIndex].index] == distance[startIndex] + 1) { if (DepthFirstSearch(&pair2[startIndex])) { pair2[startIndex] = *startNode; pair1[startIndex] = j; return true; } } } distance[startIndex] = INF; // Es gibt keinen augmentierenden Pfad, der mit startNode beginnt. return false; } return true; } // Konstruktor mit der Anzahl count1 und count2 BipartiteGraph::BipartiteGraph(int count1, int count2) { this->count1 = count1; this->count2 = count2; adjacencyList = new list<Node>[count1 + 1]; } // Diese Methode verbindet die Knoten startNode und targetNode miteinander. void BipartiteGraph::AddEdge(Node* startNode, Node* targetNode) { adjacencyList[(*startNode).index].push_back(*targetNode); } // Hauptmethode, die das Programm ausführt int main() { // Deklariert und initialisiert 4 Knoten Node node1 = Node{ 1, "A", NIL }; Node node2 = Node{ 2, "B", NIL }; Node node3 = Node{ 3, "C", NIL }; Node node4 = Node{ 4, "D", NIL }; // Verbindet Knoten des Graphen miteinander BipartiteGraph bipartiteGraph(4, 4); bipartiteGraph.AddEdge(&node1, &node2); bipartiteGraph.AddEdge(&node1, &node3); bipartiteGraph.AddEdge(&node2, &node1); bipartiteGraph.AddEdge(&node3, &node2); bipartiteGraph.AddEdge(&node4, &node2); bipartiteGraph.AddEdge(&node4, &node4); cout << "Size of maximum matching is " << bipartiteGraph.HopcroftKarp(); // Ausgabe auf der Konsole } - Hubertus th。男の子: 最適化b 。講義のスクリプト。 Augustinus Bookstoreの出版社、Aachen、ISBN 3-925038-19-1
- ↑ H. Alt、N。Blum、K。Mehlhorn、M。Paul、カンピナス大学コンピューティング研究所: 時間内に二部グラフで最大のカーディナリティマッチングを計算する
- ↑ Joao C. Setubal、カンピナス大学コンピューティング研究所: 二部マッチングアルゴリズムを使用したシーケンシャルおよびパラレルの実験結果
- ↑ geeksforgeeks: 最大マッチングのためのHopcroft – Karpアルゴリズム
Recent Comments