この章は、基本的なグラフ理論を思い出させることを意図している。 読者があらかじめグラフアルゴリズムの知識があるのなら、始めるにあたりこの章は十分であろう。 もし読者がグラフアルゴリズムの知識がないのならば、 Cormen, Leiserson, RivestのIntroduction to Algorithms のようなもっと詳しいものを薦める。
グラフは、多くの種類の問題を解くのに有効な数学的抽象化である。 基本的には、グラフは頂点と辺から構成され、辺は二つの頂点を結ぶ。 もっと正確には、 グラフ(graph)とは組(V,E)で表され、 Vは有限集合で、EはVの2項関係である。 Vは 頂点集合(vertex set) と呼ばれ、その要素を 頂点(vertex) と呼ぶ。 Eは辺の集合で、 辺(edge) とは(u,v)の組でu,vはVの要素である。 有向グラフ(directed graph)においては、 辺は順序付けられた組で、 始点(source)を 終点(target)へと接続する。 無向グラフ(undirected graph)においては、 辺は順序付けされていない組で、2つの頂点を両方向につなぐ。 つまり、無向グラフでは (u,v)と(v,u)は同じ辺の2通りの書き方である。
グラフのこの定義はいくつかの点であいまいである。 辺や頂点が何を表現するかが述べられていない。 グラフの例としては、連絡道路やハイパーリンク付きのウェブページなどを挙げることができる。 これらの詳細がグラフの定義からは除外されているのは、大きな理由がある。 それらの詳細はグラフの抽象化の中では必要な部分ではない。 詳細を定義から除外することで再利用可能な理論を構築でき、 そのことは多くの異なった種類の問題を解く際に役に立つのである。
定義にもどろう。グラフは頂点と辺の集合である。 実際の様子を見せるため、頂点に文字のラベルがついたグラフを考え、辺を単純に文字の組としよう。 ここで、有向グラフの例を次のように書くことができる。
V = {v, b, x, z, a, y } E = { (b,y), (b,y), (y,v), (z,a), (x,x), (b,x), (x,v), (a,z) } G = (V, E) |
このグラフを図示すると 図1 のようになる。 辺 (x,x) は輪(self-loop)と呼ばれる。 (b,y)と (b,y)は平行辺(parallel edges)であり、これは マルチグラフ(multigraph) でのみ許される(ただし、通常は有向グラフでも無向グラフでも許されない)。
次に似たようなグラフを示すが、今度は無向グラフである。
これは図2に図示する。
無向グラフでは輪は許されない。
上記のグラフ(から平行辺(b,y)を除いたもの)の
無向版(undirected version)である。
それはつまり、同じ頂点をもち、同じ辺から方向を除いたものを持つことを意味し、
(a,z)と(z,a)いう2つの辺は一つの辺に退化する。
また、逆を考えることもできる。
無向グラフの有向版(directed version)は、
すべての辺をそれぞれの方向を向く2つの辺で置き換えることで得られる。
ここでさらにグラフの用語を定義する。
辺(u,v)がグラフに含まれるとき、
頂点vは頂点uについて隣接している(adjacent)と言う。
有向グラフでは、辺(u,v)は
頂点uの出辺(out-edge)であり、
頂点vの入辺(in-edge)である。
無向グラフでは、辺(u,v)は
頂点uとvを
接合している(incident on)という。
図1で、
頂点yは頂点bに対して隣接している
(ただしbはyに対して隣接していない)。
辺(b,y)はbの出辺であり、yの入辺である。
図2で、
yはbに隣接していて、また逆も同様である。
辺(y,b)は頂点yとbを接合している。
有向グラフにおいて、ある頂点の出辺の数は
出次数(out-degree)と呼ばれ、
入辺の数は
入次数(in-degree)と呼ばれる。
無向グラフにおいて、ある頂点に対して接合している辺の数は
次数(degree)と呼ばれる。
図1で、頂点bの出次数は3であり、
入次数は0である。
図2では単純に頂点bの次数は2である。
グラフの路(path)とは辺の列で、
それぞれの辺の終点が次の辺の始点であるものである。
頂点uから始まり頂点vで終わる路があれば、
頂点vはuから到達可能(reachable)であるという。
路が単純(simple)であるとは、
辺の列の中でどの頂点も繰り返し現れないことである。
路<(b,x), (x,v)>は単純であるが、
路<(a,z), (z,a)>は単純ではない。
また、路<(a,z), (z,a)>は最初の頂点と最後の頂点が一致するので、
サイクル(cycle)と呼ばれる。
サイクルのないグラフは
アサイクリック(acyclic)と呼ばれる。
planar graph
とは、すべての辺が交差しないように平面上に描けるグラフのことである。
そのように描かれたものはplane graphと呼ばれる。
plane graphの面(face)とは、辺に囲まれた連結成分のことである。
planar graphの重要な特性は、
面、辺、頂点の数がオイラーの定理:|F| - |E| + |V| = 2によって関係付けられることである。
このことは、planar graphは最大でもO(|V|)個の辺しか持たないことを意味する。
データ構造を考えるときに最初に考えるべきグラフの属性は、まばらさ(sparsity)である。
まばらさとは頂点に対する相対的な辺の数である。
EがV2に近いグラフは密(dense)であると呼ばれ、
E = alpha VでalphaがVより十分に小さい場合はまばらな(sparse)グラフと呼ばれる。
密なグラフについては、通常隣接行列表現(adjacency-matrix representation)が最良の選択であり、
一方まばらなグラフについては隣接リスト表現(adjacency-list representation)が最良である。
また、まばらなグラフについては辺リスト表現(edge-list representation)も適切な状況下では記憶効率面でよい選択である。
グラフの隣接行列表現はV x Vの2次元配列である。
行列auvの要素は、辺(u,v)がグラフに含まれるかどうかを示すブーリアン値である。
図3に図1(から(b,y)を引いたもの)の隣接行列表現を表す。
保存に必要な領域はO(V2)である。
任意の辺について、アクセス、追加、除去にかかる時間はO(1)である。
頂点の追加や除去は、再割り当てとすべてのグラフのコピーが必要になり、手順数はO(V2)になる。
adjacency_matrixクラスは、
隣接行列表現によってBGLグラフインターフェースを実装する。
グラフの隣接リスト表現では、すべての頂点に対して出辺の列を保存する。
まばらなグラフでは、こうすることでメモリ領域を節約でき、必要な領域はO(V + E)だけになる。
さらに、すべての頂点の出辺にはより効果的にアクセスできる。
辺の挿入のコストはO(1)で、任意の辺へのアクセスはO(alpha)である。
ここで、alphaは行列のまばらさ(グラフ中のすべての頂点についての出辺の数の最大値)である。
図4は図1のグラフの隣接リスト表現である。
adjacency_listは隣接リスト表現の実装である。
グラフの辺リスト表現は、単純に辺の列であり、辺は頂点のIDの組で表される。
必要なメモリはO(E)だけである。
辺挿入のコストはO(1)であり、特定の辺のアクセスするのはO(E)(あまり効果的でない)である。
図5は図1のグラフの辺リスト表現である。
edge_listアダプタクラスは、辺リスト表現の実装を作るのに使うことができる。
木辺(tree edge)とは、グラフ探索アルゴリズムをグラフに適用することによって作られた探索木(またはフォレスト)の辺ことである。
辺(u,v)は木辺であるのは、辺(u,v)の探索(ビジタのexplore()メソッドにあたる)をしているときにvが最初に見つかるときである。
後退辺(back edge)とは探索木上で頂点を先祖につなぐ辺である。
したがって、辺(u,v)ではvはuの先祖である。
輪は後退辺とみなされる。
先行辺(forward edge)は木辺ではない辺(u,v)で、探索木上uを子孫vへとつなぐ。
交差辺(cross edge)とは、以上の3つのカテゴリに含まれない辺のことである。
広さ優先探索(Breadth-First Search, BFS)とは、グラフに対して横断的であり、特定の原点から到達可能な頂点をすべて探索する。
また横断する順番については、頂点のすべての近傍を探索してから近傍の近傍の探索へと進む。
広さ優先探索について考えるには、例えば水溜りに石を落としたときに波が放射状に広がるように拡散すると思えばよい。
同じ「波」の中の頂点は原点から同じ距離にある。
頂点は最初にアルゴリズムによって遭遇するときに発見される(discovered)と言う。
頂点は、その近傍がすべて探索されたときに完了した(finished)と言われる。
これらをわかりやすくする例がある。
グラフを図6に示し、そのBFSにおける発見と完了の順番をその下に示す。
sから開始して、最初はrとw(sの近傍)にたどり着く。
sの両方の希望に到達してから、
rの近傍(頂点v)に到達し、wの近傍tとxに到達する
(rとwの順序は意味を持たない)。
最後にtとxの近傍、uとyに到達する。
今グラフ上のどこにいるか、次にどこの頂点に行くかをアルゴリズムが把握するために、
BFSは頂点に色を塗る。塗る色を置く場所は、グラフの中でもよいし、アルゴリズムに引数として渡すこともできる。
A depth first search (DFS) visits all the vertices in a graph. When
choosing which edge to explore next, this algorithm always chooses to
go ``deeper'' into the graph. That is, it will pick the next adjacent
unvisited vertex until reaching a vertex that has no unvisited
adjacent vertices. The algorithm will then backtrack to the previous
vertex and continue along any as-yet unexplored edges from that
vertex. After DFS has visited all the reachable vertices from a
particular source vertex, it chooses one of the remaining undiscovered
vertices and continues the search. This process creates a set of
depth-first trees which together form the depth-first
forest. A depth-first search categorizes the edges in the graph
into three categories: tree-edges, back-edges, and forward or
cross-edges (it does not specify which). There are typically many
valid depth-first forests for a given graph, and therefore many
different (and equally valid) ways to categorize the edges.
One interesting property of depth-first search is that the discover
and finish times for each vertex form a parenthesis structure. If we
use an open-parenthesis when a vertex is discovered, and a
close-parenthesis when a vertex is finished, then the result is a
properly nested set of parenthesis. Figure 7 shows
DFS applied to an undirected graph, with the edges labeled in the
order they were explored. Below we list the vertices of the graph
ordered by discover and finish time, as well as show the parenthesis structure. DFS is used as the kernel for several other graph
algorithms, including topological sort and two of the connected
component algorithms. It can also be used to detect cycles (see the Cylic Dependencies
section of the File Dependency Example).
The minimum-spanning-tree problem is defined as follows: find
an acyclic subset T of E that connects all of the vertices
in the graph and whose total weight is minimized, where the
total weight is given by
One of the classic problems in graph theory is to find the shortest
path between two vertices in a graph. Formally, a path is a
sequence of vertices <v0,v1,...,vk>
in a graph G = (V, E) such that each vertex is connected to the
next vertex in the sequence (the edges
(vi,vi+1) for i=0,1,...,k-1 are in
the edge set E). In the shortest-path problem, each edge is
given a real-valued weight. We can therefore talk about the weight
of a path
There are several variants of the shortest path problem. Above we
defined the single-pair problem, but there is also the
single-source problem (all shortest paths from one vertex to
every other vertex in the graph), the equivalent
single-destination problem, and the all-pairs problem.
It turns out that there are no algorithms for solving the single-pair
problem that are asymptotically faster than algorithms that solve the
single-source problem.
A shortest-paths tree rooted at vertex in graph G=(V,E)
is a directed subgraph
A flow network is a directed graph G=(V,E) with a
source vertex s and a sink vertex
t. Each edge has a positive real valued capacity
function c and there is a flow function f
defined over every vertex pair. The flow function must satisfy three
contraints:
f(u,v) <= c(u,v) for all (u,v) in V x V (Capacity constraint)
The flow of the network is the net flow entering the
sink vertex t (which is equal to the net flow leaving the
source vertex s).
|f| = sumu in V f(u,t) = sumv in V f(s,v)
The residual capacity of an edge is r(u,v) = c(u,v) -
f(u,v). The edges with r(u,v) > 0 are residual edges
Ef which induce the residual graph Gf
= (V, Ef). An edge with r(u,v) = 0 is
saturated.
The maximum flow problem is to determine the maximum
possible value for |f| and the corresponding flow values for
every vertex pair in the graph.
A flow network is shown in Figure
8. Vertex A is the source vertex and H is the target
vertex.
There is a long history of algorithms for solving the maximum flow
problem, with the first algorithm due to Ford and Fulkerson. The
best general purpose algorithm to date is the push-relabel algorithm
of Goldberg
which is based on the notion of a preflow introduced by
Karzanov.
V = {v, b, x, z, a, y }
E = { (b,y), (y,v), (z,a), (b,x), (x,v) }
G = (V, E)
Graph Data Structures
Adjacency Matrix Representation
Adjacency List Representation
Edge List Representation
Graph Algorithms
Graph Search Algorithms
Breadth-First Search
発見の順番: s r w v t x u y
完了の順番: s r w v t x u y
Depth-First Search
order of discovery: a b e d c f g h i
order of finish: d f c e b a
parenthesis: (a (b (e (d d) (c (f f) c) e) b) a) (g (h (i i) h) g)
Minimum Spanning Tree Problem
T is called the spanning tree.
Shortest-Paths Algorithms
The shortest path weight from vertex u to v is then
delta (u,v) = infinity otherwise.
A shortest path is any path who's path weight is equal to the
shortest path weight.
Network Flow Algorithms
f(u,v) = - f(v,u) for all (u,v) in V x V (Skew symmetry)
sumv in V f(u,v) = 0 for all u in V - {s,t} (Flow conservation)
Copyright © 2000-2001
Jeremy Siek, Indiana University (jsiek@osl.iu.edu)