プログラミング言語i 第10回 最短経路問題 · プログラミング言語i 第10回...
TRANSCRIPT
プログラミング言語I 第10回
短経路問題
埼玉大学工学部電気電子システム工学科
伊藤 和人
Copyright © 2008 Kazuhito Ito
短経路問題とは
始点から終点へ行く経路が複数通りある場合に、 も短い経路を見つける問題
経路の短さの決め方によって様々な応用
Copyright © 2008 Kazuhito Ito
短経路問題の応用例
カーナビゲーション
現在地から目的地まで 短時間のルート
経路=道路
交差点において走る道路を変更してもよい
経路の短さ=所要時間の短さ
鉄道乗り換え案内
始発駅から目的駅まで料金 低のルート
経路=路線
駅において路線を乗り換えてもよい
経路の短さ=料金の安さ
Copyright © 2008 Kazuhito Ito
問題の定式化
定式化: 問題の意味が変化しないことに注意して、明確な形式に問題を整理すること
条件と解の性質を明確にする
あいまいな点をなくす
さらに、問題を解くプログラムが容易に作成できるような定式化を行うことが重要
Copyright © 2008 Kazuhito Ito
グラフ
プログラミングに適した問題定式化の道具としてよく用いられる
1個以上の点(ノード)と2つの点を結ぶ枝からなる
点 枝
グラフの例
Copyright © 2008 Kazuhito Ito
グラフ(その2)点を共有する枝の集合をパス(path、経路)という
パスの始点と終点を定める
一般に始点と終点が同じパスは複数通り存在する(1通りしか存在しない場合や、1つも存在しない場合あり)
始点終点
パス
Copyright © 2008 Kazuhito Ito
グラフ(その3)
枝に重みを与える
パスの重みは、枝重みの和とする
始点終点
パス: 重み12
2
1
9
63
5
2
2
始点・終点が同じ複数通りのパス
パスによって重みは異なる
枝重み
Copyright © 2008 Kazuhito Ito
短経路問題の定式化
グラフによって問題を入力 (始点、終点、枝重み)
鉄道乗り換え案内の場合
乗換駅を点、枝重みを料金とすればよい
横浜
品川東京 上野
赤羽 大宮
池袋新宿渋谷
重み 小のパスを見つける
280円
260円
160円150円 150円 150円
290円160円
160円150円160円
2,750円
Copyright © 2008 Kazuhito Ito
小値原理
始点sから終点tへの 短経路上の点をvとすると、パスp(s,v)とパスp(v,t)はともに
短経路である
s始点 終点
sからtへの 短経路
tsからvへの短経路
vからtへの短経路
v
終点以外の 短経路を順次求めて、終的に終点への 短経路を求める
Copyright © 2008 Kazuhito Ito
問題の分類
枝重みが0または正の場合
枝重みが0、正、負で負ループがない場合
枝重みが0、正、負で負ループがある場合
ループ: 始点と終点が一致したパス
負ループ: 枝重み和が負のループ
負ループ: 重み-2
2
1
-8
13
5
2
0枝重みが0、正、負で負ループがある場合
Copyright © 2008 Kazuhito Ito
短経路アルゴリズム1
枝重みが0または正の場合を考える
始点sから点bへの短経路が1である
と決定できる
なぜか?
21
s36
a
d
b
c
0
3 2
41 0
2
始点
始点sから他の点への 短経路を求める
21
s36
a
d
b
c
0
3 2
41 0
2
始点
Copyright © 2008 Kazuhito Ito
短経路アルゴリズム1(その2)
枝重みが0または正の場合
21
s36
a
d
b
c
0
3 2
41 0
2
枝重みが0または正であるので、点a, c, dを経由するどのパスも重みが1以上となるため
始点
2
3
6
cを経由パス重みは3以上
dを経由パス重みは6以上
aを経由パス重みは2以上1
Copyright © 2008 Kazuhito Ito
1
短経路アルゴリズム1(その3)次に経路が 短な点を求める
始点
21
s36
a
d
b
cf
e0
3 2
41 0
2
2 4
53
6
121
s36
a
d
b
cf
e
3 2
41 0
2
02 4→2
53
6
1
22
1
s36
a
d
b
cf
e
3 2
41 0
2
0 2
53
6
1
2 22
1
s36
a
d
b
cf
e
3 2
41 0
2
0
5→43
6
Copyright © 2008 Kazuhito Ito
短経路アルゴリズム1(その4)
アルゴリズムのポイント
短経路長の定まった点から、さらに枝1本で到達する点のうち、経路長 短の点は、 短経路と決定できる
1
2
s
7
3
42
2
4
53
4
6
6
7
短経路長の決まっている点
さらに枝1本で到達する点
経路長が 小⇒ 短経路を決定できる点
ダイクストラ法
Copyright © 2008 Kazuhito Ito
C言語によるグラフ表現1
2次元配列を用いる方法
枝が結ぶ2点(i,j)
配列要素e[i][j]が枝を表す
i⇒j と j⇒i を区別しない場合e[j][i] = e[i][j] とする
ji
e[i][j]=1: 枝あり
e[i][j]=0: 枝なし
Copyright © 2008 Kazuhito Ito
C言語によるグラフ表現1(続き)
枝(i,j)の重み ・・ w[i][j]が記憶
i⇒j と j⇒i を区別しない場合w[j][i] = w[i][j] とする
ji
w[i][j]
Copyright © 2008 Kazuhito Ito
ダイクストラ法の実装
int s, minLen, j, m, u[N], f[N]={0};for( j=0 ; j<N ; j++ ) u[j] = 9999;s = 0; u[s] = 0; for( m=1 ; m<N ; m++ ){
f[s] = 1;for( j=0 ; j<N ; j++ ){
if( e[s][j] != 1 ) continue;if( u[s]+w[s][j] < u[j] ) u[j]=u[s]+w[s][j];
}minLen = 9999;for( j=0 ; j<N ; j++ ){
if( f[j] == 1 ) continue;if( u[j] < minLen ){ minLen = u[j]; s = j; }
}}
十分大きな正数
枝(s,j)が存在しない
始点
s=0と
する
短経路既定の点は除外
点sは 短経路決定
点数N
点sは経路長が 短
点jへの経路長を更新
Copyright © 2008 Kazuhito Ito
長さだけでなく経路を記録
int s, minLen, j, m, u[N], f[N]={0}, prev[N];for( j=0 ; j<N ; j++ ) u[j] = 9999;s = 0; u[s] = 0; for( m=1 ; m<N ; m++ ){
f[s] = 1;for( j=0 ; j<N ; j++ ){
if( e[s][j] != 1 ) continue;if( u[s]+w[s][j] < u[j] ){
u[j]=u[s]+w[s][j]; prev[j]=s; } /* jの直前はs */}minLen = 9999;for( j=0 ; j<N ; j++ ){
if( f[j] == 1 ) continue;if( u[j] < minLen ){ minLen = u[j]; s = j; }
}}
十分大きな正数
枝(s,j)が存在しない
始点
s=0と
する 点sは 短経路決定
点数N
点sは経路長が 短
点jへの経路長を更新
短経路既定の点は除外
Copyright © 2008 Kazuhito Ito
ダイクストラ法の計算複雑度
経路長 短の点を1つ選び経路を決定
その点から1本の枝で接続する点について経路長を調べなおす
すべての点について経路が決定するまで繰り返す
)( 2NO
Copyright © 2008 Kazuhito Ito
プログラムの実行時間
ノード数Nとプログラム実行時間の関係
0.01
0.10
1.00
10.00
100.00
100 1000 10000 100000N
秒
PentiumIV2.4GHz512MBメモリ
予想
N=8000約500Mバイト
N=7000約400Mバイト
1つのノード当たり4本の枝の場合
Copyright © 2008 Kazuhito Ito
プログラムの問題点
配列による枝表現はメモリを大量に必要とし、かつ効率が悪い
010000000000000000000000000000000000000000000000000000101000000000000000000011000000000000000000000000000000010110000000000000000000000000000000000000000000000000001010000000000000000000000000000000000000000000000000001101000000000000000000000000000000000000000000000000000010110000000000000000000000000000000000000000000000000001010000000000000000000000000000000000000001000000000001100000000000000000000000000000000000000100000100000000000110000000000000000000000000000000000000000100000000001010000000000000000000000000000000000000000000000000001101000000000000000000000000000000000000000010000000000010100000000000000000000000000000000000100000000000000001010000000000000000000000000000000000000000000000000000101001000000000000000000000000000000000010000000000000010100100000000000000000000000000000000000000000000000001000000000000000000000000000000010100000000000000000000001000000100000000000000000000101000000000000000000010010100000000000000000000000000000000000000000000000001001010000000010000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000010100000000000000000000000000010000000000000000000000001000000000001001000000000000000000010000000000000000000001000000000000000000000000001000010000000000000000000010100000000000000000000000000000…
j
i
枝の有無を表す2次元配列e[i][j]の例
0がほとんど
Copyright © 2008 Kazuhito Ito
配列による枝表現の問題
枝の有無(e[i][j]==1か否か)を調べる処理が必要
枝が無くても(e[i][j]=0)を記憶する必要があるためメモリを大量に消費し、速度低下(スラッシング)
配列に代わる、不規則なデータを効率よく記録する方式が必要
リスト構造
Copyright © 2008 Kazuhito Ito
リストの管理
リストの要素データ領域の他に、次の要素を指すポインタを備える
ポインタを用いて要素をつなげる=リスト
データ領域
ポインタ
要素
次の要素を指す
Copyright © 2008 Kazuhito Ito
リストの実装
構造体によって、データ領域と次要素へのポインタをまとめて管理する
例
要素の構造体型を宣言
typedef struct Edge {struct Edge *next; int i,j; // 点1, 点2int w; // 枝重み
} EDGE;
次要素へのポインタ
データ領域
例
リストの先頭を指すポインタを宣言
EDGE *edge_top = NULL;
Copyright © 2008 Kazuhito Ito
リストの実装(その2)
リストの例
リストを順にたどる処理
edge_topNULL
点2枝重み
点2枝重み
点2枝重み
点1 点1 点1
3本の枝を記録するリスト
リストの末尾ではnextメンバはNULL
EDGE *ep;for( ep=edge_top ; ep != NULL ; ep=ep->next ){… /* リスト要素に対する処理 */
}
Copyright © 2008 Kazuhito Ito
ダイクストラ法における枝リスト
短経路が決まるたびに、その点から枝1本でつながる点の経路長更新
1
2
s
7
3
42
2
4
53
4
6
6
短経路長の決まっている点
経路長を更新する点
新たに 短経路長の決まった点
各点に接続する枝リストがあると便利
7
Copyright © 2008 Kazuhito Ito
ダイクストラ法のための枝リスト
各点に接続する枝のリストNULL
点2枝重み
点2枝重み
点2枝重み
0 0 0edge[0]
NULL
点2枝重み
点2枝重み
点2枝重み
1 1 1edge[1]
edge[2]
点2枝重み
1
点ごとに接続する枝数は変化
点2枝重み
2
点0に接続する枝のリスト
点1に接続する枝のリスト
ダイクストラ法と組合わせる場合、メンバ「点1」は不要
Copyright © 2008 Kazuhito Ito
リストを用いたダイクストラ法
int s, minLen, j, m, u[N], f[N]={0};EDGE edge[N], *ep; /* 枝リストを設定する処理は省略 */for( j=0 ; j<N ; j++ ) u[j] = 9999;s = 0; u[s] = 0; for( m=1 ; m<N ; m++ ){
f[s] = 1;for( ep=edge[s] ; ep!=NULL ; ep=ep->next ){
if( u[s]+ep->w < u[ep->j] ){u[ep->j]=u[s]+ ep->w;}
}minLen = 9999;for( j=0 ; j<N ; j++ ){
if( f[j] ) continue;if( u[j] < minLen ){ minLen = u[j]; s = j; }
}}
十分大きな正数
始点
s=0と
する
短経路既定の点は除外
点sは 短経路決定
点数N
点sは経路長が 短
sに接続する枝を順に調べる
点ep->jへの経路長を更新
Copyright © 2008 Kazuhito Ito
プログラムの実行時間
ノード数Nとプログラム実行時間の関係
0.01
0.10
1.00
10.00
100.00
100 1000 10000 100000N
秒
PentiumIV2.4GHz512MBメモリ
リスト構造利用
配列利用N=8000約500Mバイト
N=7000約400Mバイト
N=40000約1Mバイト
Copyright © 2008 Kazuhito Ito
短経路アルゴリズム2
負の枝重みが存在する場合
21
s36
a
d
b
c
0
3 -1
-41 0
-2
始点
ダイクストラ法では 短経路を見つけられない
なぜか?
まだ 短経路の決まっていない点を経由した方が経路長が短くなるパスが存在するかもしれないため
Copyright © 2008 Kazuhito Ito
Bellman方程式
点jの 短経路長をujとするとき、
ujが満たす関係式・・・Bellman方程式
{ } sjwuu ijii
j ≠∀+= min
0=su
wiji j始点 s
uiuj
小値原理より
Copyright © 2008 Kazuhito Ito
Bellman方程式を解く
Bellman方程式の解= 短経路
漸化的に解く
u[i]が更新されたら、枝(i,j)が存在する点jについてu[j]を更新する
すべての点jについてu[j]が変化しなくなったら、解が得られている
Bellman-Ford法
Copyright © 2008 Kazuhito Ito
リストを用いたBellman-Ford法実装
int s,update,minL,i,u[N], prev[N];EDGE *edge_top, *ep;
for( i=0 ; i<N ; i++ ) u[i] = 9999;s = 0; u[s] = 0;do {
update = 0;for( ep=edge_top ; ep != NULL ; ep=ep->next ){
if( u[ep->i]+ep->w < u[ep->j] ){u[ep->j] = u[ep->i]+ ep->w;prev[ep->j] = ep->i;update = 1;
}}
} while ( update );
十分大きな正数
/*リストを正しく作成する必要あり(ここでは省略) */
始点
s=0と
する
経路を記録
経路長が短くなったら更新フラグを1に
経路長更新の場合再度繰り返し
Copyright © 2008 Kazuhito Ito
do - while文
条件が成り立つ間、文を実行
do 文 while( 式 );
意味: まず1回文を実行する。 式が真の間、文を実行。
式
文
True
False
式
文
True
False
do-while文の実行の様子 “while(式) 文;”の実行の様子
Copyright © 2008 Kazuhito Ito
Bellman-Ford法の計算複雑度
すべての枝を順に調査
経路長が更新されたら、再度調査実行
負ループがなければ 短経路を構成する枝数はN未満・・・更新は高々N-1回
)(NEO実行時間
Copyright © 2008 Kazuhito Ito
負ループのある 短経路問題
短経路は不定⇒ 負ループを繰り返すごとに経路長は減少
「経路にループを含まない」という制約を与えると意味のある問題として定式化される
効率よく 短経路を求めるアルゴリズムは見つかっていない
負ループが存在する場合の 短経路問題は負ループが存在しない場合の問題とは性格が異なる
Copyright © 2008 Kazuhito Ito
組み合わせ 適化問題
ある条件を満足する解候補のうち、ある評価尺度が 適になる解を選ぶ問題
例: 短経路問題
条件: 始点から終点への連続した枝集合(経路)
評価尺度: 枝重み和が小さい
Copyright © 2008 Kazuhito Ito
組み合わせ 適化問題の困難さ
組み合わせ 適化問題を3つに分類
P: 多項式可解な問題
問題サイズの多項式オーダの手数で解が得られる⇒計算複雑度が多項式オーダの解法が存在
NP: 非決定的多項式可解な問題
都合良く選択肢を選ぶと、問題サイズの多項式オーダの手数で解が得られる
PでもNPでもない問題
PやNPよりも難しい問題
Copyright © 2008 Kazuhito Ito
問題の困難さ
組み合わせ 適化問題の分類とその関係
P問題の例ソート(並べ替え)短経路問題
オペレーションズ・リサーチ
NP
P
全組み合わせ問題
Copyright © 2008 Kazuhito Ito
NP問題
NP: 非決定的多項式可解な問題
都合良く選択肢を選ぶと、問題サイズの多項式オーダの手数で解が得られる
工学的に有用な問題が多数含まれる
負ループを含む 短経路問題
セールスマンの巡回問題
タスク・スケジューリング問題 など
Copyright © 2008 Kazuhito Ito
セールスマンの巡回問題
問題の定義「N個の都市を順に訪問するための旅費が
小になる訪問順を求めよ」
都市間の旅費は非負
短経路( 小費用)を求める問題
一見すると 短経路問題として解けそう?
すべての訪問順を列挙して 小費用の順路を求める方法しか知られていない ⇒ )!(NO
Copyright © 2008 Kazuhito Ito
NP問題とP問題
NP問題の解には、今のところ非多項式オーダの手数が必要
多項式オーダの解法が存在しないことは証明されていない
もしかするとNP=Pかもしれない
NP問題のどれか1つについて多項式オーダの解法が見つかればNP=P
すべてのNP問題が多項式オーダで解ける!
Copyright © 2008 Kazuhito Ito
まとめ
問題定式化とグラフ
短経路問題
効率よい解法
ダイクストラ法
Bellman-Ford法
リスト構造
組み合わせ 適化問題とNP