プリフロープッシュ法

プリフロープッシュ法（プリフロープッシュほう、プッシュ再ラベル法、preflow–push algorithm, push–relabel algorithm）とは、数理最適化においてフローネットワークにおける最大流を求めるアルゴリズムである。プッシュ・再ラベルの名はプッシュ操作と再ラベル操作の二つの手続きを行うことに由来する。実行を通じて、プリフロープッシュ法はプリフローを保持しながら、再ラベル操作により求まる可能ネットワークの下で近傍の頂点間にフローを流すプッシュ操作によって最大流を求める。同じ最大流問題に対するアルゴリズムのフォード・ファルカーソン法ではソースからシンクまでの路からフローを増やしていく大域的な増加路を求めるアルゴリズムである。
プリフロープッシュ法は最大流問題に対して最も効率の良いアルゴリズムの一つとして知られている。汎用プリフロープッシュ法においては時間計算量が強多項式時間の O(V 2E) で動作することが知られており、計算量 O(VE 2) で動作するエドモンズ・カープ法よりも効率の良い解法とされている。類似の解法の中にはより高速に動作するアルゴリズムも存在する。highest label node selection規則に従った方法では計算量が  オーダーで動作することが最大流問題に対するベンチマーク問題の実験から求まっている。動的木を使用したプリフロープッシュ法では時間計算量は O(VElog(V 2/E)) に達するが、実用上の面では効率が悪くなるdate=2024-12。
またプリフロープッシュ法はminimum cost flowに対して拡張することができる。距離ラベルの概念はより効率的に増加道を求めることができ、これとプリフロープッシュ法を組み合わせることでより高い性能を発揮するアルゴリズムを使用することができる。

**歴史**
プリフローの概念は1974年にAlexander V. Karzanovによって Soviet Mathematical Dokladi 15 に掲載したことから始まる。プリフロー法は増加道の距離を求める際にラベリングシステムを使用せず、プッシュ操作により求めるアルゴリズムである。
プリフロープッシュ法はAndrew V. Goldbergとロバート・タージャンによって提案されたアルゴリズムである。プリフロープッシュ法は1986年11月の STOC '86: Proceedings of the eighteenth annual ACM symposium on Theory of computing において始めて公表され、1988年に ACM の学術論文誌に掲載された。両者の論文では計算量 O(V 2E) で終了する汎用解法、計算量 O(V 3) の段階的解法、計算量 O(VE log(V 2/E)) で動作する動的木を使用し、並列・分散実装化された解法が提案されている。ゴールドバーグ＝タージャンはYossi Shiloach・Uzi Vishkinの並列最大流アルゴリズムにおける距離ラベルに上記の技法を適用した解法を公表した。

**実装例**
#include 
#include 
#define NODES 6
#define MIN(X,Y) ((X)  0) {
min_height = MIN(min_height, heightv);
heightu = min_height + 1;
}
}
};
void discharge(const int * const * C, int ** F, int *excess, int *height, int *seen, int u) {
while (excessu > 0) {
if (seenu  0) && (heightu > heightv)) {
push(C, F, excess, u, v);
} else {
seenu += 1;
}
} else {
relabel(C, F, height, u);
seenu = 0;
}
}
}
void moveToFront(int i, int *A) {
int temp = Ai;
int n;
for (n = i; n > 0; n--) {
An = An-1;
}
A0 = temp;
}
int pushRelabel(const int * const * C, int ** F, int source, int sink) {
int *excess, *height, *list, *seen, i, p;
excess = (int *) calloc(NODES, sizeof(int));
height = (int *) calloc(NODES, sizeof(int));
seen = (int *) calloc(NODES, sizeof(int));
list = (int *) calloc((NODES-2), sizeof(int));
for (i = 0, p = 0; i  old_height) {
moveToFront(p, list);
p = 0;
} else {
p += 1;
}
}
int maxflow = 0;
for (i = 0; i 
def relabel_to_front(C, source: int, sink: int) -> int:
n = len(C)  # C is the capacity matrix
F = 0 * n for _ in range(n)
# residual capacity from u to v is Cuv - Fuv
height = 0 * n  # height of node
excess = 0 * n  # flow into node minus flow from node
seen   = 0 * n  # neighbours seen since last relabel
# node "queue"
nodelist = i for i in range(n) if i != source and i != sink
def push(u, v):
send = min(excessu, Cuv - Fuv)
Fuv += send
Fvu -= send
excessu -= send
excessv += send
def relabel(u):
# Find smallest new height making a push possible,
# if such a push is possible at all.
min_height = ∞
for v in range(n):
if Cuv - Fuv > 0:
min_height = min(min_height, heightv)
heightu = min_height + 1
def discharge(u):
while excessu > 0:
if seenu  0 and heightu > heightv:
push(u, v)
else:
seenu += 1
else:  # we have checked all neighbours. must relabel
relabel(u)
seenu = 0
heightsource = n  # longest path from source to sink is less than n long
excesssource = ∞  # send as much flow as possible to neighbours of source
for v in range(n):
push(source, v)
p = 0
while p  old_height:
nodelist.insert(0, nodelist.pop(p))  # move to front of list
p = 0  # start from front of list
else:
p += 1
return sum(Fsource)

**脚注 ==