• 検索結果がありません。

最短路の問題

N/A
N/A
Protected

Academic year: 2021

シェア "最短路の問題"

Copied!
16
0
0

読み込み中.... (全文を見る)

全文

(1)

アルゴリズムとデータ構造 アルゴリズムとデータ構造 アルゴリズムとデータ構造 アルゴリズムとデータ構造

2012 2012 年 年 7 7 月 月 17 17 日 日

酒居敬一 酒居敬一

(sakai.keiichi@kochi-tech.ac.jp)(sakai.keiichi@kochi-tech.ac.jp)

http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/2012/index.html http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/2012/index.html

1

(2)

最短路の問題

(260ページ)

• 最短路問題

– グラフの2頂点間を結ぶ道のうちで辺の重みの 和が最小のものを求める問題。

• 対象は辺に重みのついた有向グラフ

• 問題は大きく2種類ある

1. 出発点を1つに固定して、そこから他のすべて の頂点への最短路を求める。

2. すべての2頂点の組み合わせに対して、最短路 を求める。

1の方法をすべての頂点に適用するのが最善。

2

(3)

図4

. .

1 アルゴリズムの動き

4 3

単一出発点の問題

(261ページ)

• Dijkstraのアルゴリズム

– 各頂点への最短路を出発点に近いところか らひとつづつ確定する。

10

最小の距離を持つ頂点は確定。

他から来るにしても重みが負の 経路がなければ、距離は減らない。

10

出発点

確定した頂点から行くことのできる 頂点への距離を計算。

3

(4)

public class WeightedNode<E> { private E value; //

頂点の値

private Map<WeightedNode<E>, Integer> edges; //

 有向辺

private boolean visited;

private int distance;

public WeightedNode(E value, Map<WeightedNode<E>, Integer> edges) { this.value = value;

this.edges = edges;

reset();

}

public void reset(){

this.visited = false;

this.distance = Integer.MAX_VALUE;

this.edges.clear();

}

public E getValue() { //

頂点の値を得る。

return value;

}

public Map<WeightedNode<E>, Integer> getEdges(){

return this.edges;

}

//

アクセッサは、このスライドでは省略した。実際には存在する。

public void connectTo(WeightedNode<E> to, int weight){

this.edges.put(to, weight); //

有向辺を追加

}

} 4

辺に重みを持たせるため、Mapを使う。

辺の重み

出発点からの距離

本日使う頂点のデータ構造

(5)

public class Dijkstra {

public static <E> void search(Collection<WeightedNode<E>> graph, WeightedNode<E> start){

Set<WeightedNode<E>> visited = new HashSet<WeightedNode<E>>();

Set<WeightedNode<E>> unreached = new HashSet<WeightedNode<E>>(graph);

start.setDistance(0);

while( !unreached.isEmpty() ){

int min_distance = Integer.MAX_VALUE;

WeightedNode<E> min_node = null;

for(WeightedNode<E> node: unreached){

if(node.getDistance() < min_distance){

min_distance = node.getDistance();

min_node = node;

} }

unreached.remove(min_node);

visited.add(min_node);

for(Map.Entry<WeightedNode<E>,Integer> edge: min_node.getEdges().entrySet()){

if(unreached.contains(edge.getKey())){

edge.getKey().setDistance(

Math.min(edge.getKey().getDistance(),

min_node.getDistance() + edge.getValue()));

} } } }

} 5

集合U

教科書263ページの擬似プログラムをJavaで実装

集合V

集合Uから最小の距離をもつ頂点を探索

(6)

private static WeightedNode<Character> nodeA = new WeightedNode<Character>('A', new HashMap<WeightedNode<Character>, Integer>());

private static WeightedNode<Character> nodeB = new WeightedNode<Character>('B', new HashMap<WeightedNode<Character>, Integer>());

private static WeightedNode<Character> nodeC = new WeightedNode<Character>('C', new HashMap<WeightedNode<Character>, Integer>());

private static WeightedNode<Character> nodeD = new WeightedNode<Character>('D', new HashMap<WeightedNode<Character>, Integer>());

private static WeightedNode<Character> nodeE = new WeightedNode<Character>('E', new HashMap<WeightedNode<Character>, Integer>());

private static WeightedNode<Character> nodeF = new WeightedNode<Character>('F', new HashMap<WeightedNode<Character>, Integer>());

private static Collection<WeightedNode<Character>> test_data = new LinkedList<WeightedNode<Character>>();

static {

test_data.add(nodeA);

test_data.add(nodeB);

test_data.add(nodeC);

test_data.add(nodeD);

test_data.add(nodeE);

test_data.add(nodeF);

}

public static void main(String[] args) { System.out.println("4.5.1");

for(WeightedNode<Character> node: test_data){

node.reset();

}

nodeA.connectTo(nodeB, 6);

nodeA.connectTo(nodeC, 4);

nodeB.connectTo(nodeD, 3);

nodeC.connectTo(nodeE, 3);

nodeC.connectTo(nodeF, 6);

nodeE.connectTo(nodeB, 2);

nodeE.connectTo(nodeD, 1);

search(test_data, nodeA);

System.out.println("頂点" + nodeA.getValue() + "からの距離");

for(WeightedNode<Character> node: test_data){

System.out.println("頂点" + node.getValue() + ": " + node.getDistance());

} }

6

4.5.1

頂点 A からの距離 頂点

A: 0

頂点

B: 6

頂点

C: 4

頂点

D: 8

頂点

E: 7

頂点

F: 10

ひとつずつ確定する作戦の前提として、

辺の重みは正の値である。

(7)

263ページの実装では、集合Uから最小の距離をもつ  頂点を線形探索し、集合Uから移動していた。

そもそも、線形探索は時間計算量が多い。

そこで、集合Uの替わりにヒープを使うことを考える。

ただし、リストのようなデータ構造でヒープを実装するのは

 データ操作に必要な時間計算量の多さから不適切である。

ヒープとしては頂点への参照を入れた配列を使うことにする

最小の距離を持つ頂点をヒープから移動し、ヒープを再構成す 。 る。

実装は省略しますが、263ページの擬似プログラムが単純なの で、 それを理解してみるといいだろう。それから267ページを理解 する。

7

(8)

public class DijkstraArray {

public static <E> void search(WeightedNode<E>[] nodes, int start, int[][] weights){

nodes[start].setDistance(0);

for(int step = 0; step < nodes.length; step++){

int min = Integer.MAX_VALUE;

int p = 0;

for(int x = 0; x < nodes.length; x++){

if( !nodes[x].isVisited() && (nodes[x].getDistance() < min)){

p = x;

min = nodes[x].getDistance();

} }

if(min == Integer.MAX_VALUE){

throw new IllegalArgumentException("

グラフが連結でない。

");

}

nodes[p].setVisited(true);

for(int x = 0; x < nodes.length; x++){

if(weights[p][x] == Integer.MAX_VALUE){

continue;

}

nodes[x].setDistance(

Math.min(nodes[x].getDistance(),

nodes[p].getDistance() + weights[p][x]));

} } }

}

265ページのプログラムのJava実装

8

頂点の配列と、頂点同士の接続行列

(9)

private static WeightedNode<Character> nodeA = new WeightedNode<Character>('A');

private static WeightedNode<Character> nodeB = new WeightedNode<Character>('B');

private static WeightedNode<Character> nodeC = new WeightedNode<Character>('C');

private static WeightedNode<Character> nodeD = new WeightedNode<Character>('D');

private static WeightedNode<Character> nodeE = new WeightedNode<Character>('E');

private static WeightedNode<Character> nodeF = new WeightedNode<Character>('F');

@SuppressWarnings("unchecked")

private static WeightedNode<Character>[] test_data = new WeightedNode[] { nodeA, nodeB, nodeC, nodeD, nodeE, nodeF

};

public static void main(String[] args) { System.out.println("4.5.1");

for(WeightedNode<Character> node: test_data){

node.reset();

}

int[][] weights = new int[test_data.length][test_data.length];

for(int[] from: weights){

for(int i = 0; i < from.length; i++){

from[i] = Integer.MAX_VALUE;

} }

weights[0][1] = 6;

weights[0][2] = 4;

weights[1][3] = 3;

weights[2][4] = 3;

weights[2][5] = 6;

weights[4][1] = 2;

weights[4][3] = 1;

search(test_data, 0, weights);

System.out.println("頂点" + nodeA.getValue() + "からの距離");

for(WeightedNode<Character> node: test_data){

System.out.println("頂点" + node.getValue() + ": " + node.getDistance());

} }

9

4.5.1

頂点 A からの距離 頂点

A: 0

頂点

B: 6

頂点

C: 4

頂点

D: 8

頂点

E: 7

頂点

F: 10

これもDijkstraのアルゴリズムなので、

ひとつずつ確定する作戦の前提として、

辺の重みは正の値である。

(10)

全頂点間の問題

(270ページ)

• 単一の出発点のアルゴリズムを、全頂点 に 順に適用する方法がひとつ。

• 重み行列が与えられるとき、Floyd の アルゴリズムを使って全頂点間の距離を 求める方法もがある。

10

(11)

public class Floyd {

public static <E> void search(int[][] weights, int[][] distances){

for(int i = 0; i < weights.length; i++){

distances[i] = weights[i].clone();

}

for(int k = 0; k < weights.length; k++){

for(int i = 0; i < weights.length; i++){

if(distances[i][k] == Integer.MAX_VALUE){

continue; //

未接続

or

未計算

}

for(int j = 0; j < weights[i].length; j++){

if(distances[k][j] == Integer.MAX_VALUE){

continue; //

未接続

or

未計算

}

int w = distances[i][k] + distances[k][j];

if(w < distances[i][j]){

distances[i][j] = w;

} } } } } }

11

頂点kを経由して、頂点iから頂点jに至る道があれば、

距離をあらわす行列にその距離を計算し格納する。

270ページのプログラム Floydのアルゴリズム

(12)

public static void main(String[] args) { System.out.println("

4.5.1");

int[][] weights = new int[6][6];

for(int i = 0; i < weights.length; i++){

for(int j = 0; j < weights[i].length; j++){

weights[i][j] = Integer.MAX_VALUE;

}

weights[i][i] = 0;

}

weights[0][1] = 6;

weights[0][2] = 4;

weights[1][3] = 3;

weights[2][4] = 3;

weights[2][5] = 6;

weights[4][1] = 2;

weights[4][3] = 1;

int[][] distances = new int[weights.length][];

search(weights, distances);

System.out.println("

頂点間の距離

");

for(int i = 0; i < distances.length; i++){

for(int j = 0; j < distances[i].length; j++){

if(distances[i][j] == Integer.MAX_VALUE){

System.out.print("- ");

} else {

System.out.print(distances[i][j] + " ");

} }

System.out.println();

}

} 12

4.5.1

頂点間の距離

0 6 4 8 7 10 - 0 - 3 - - - 5 0 4 3 6 - - - 0 - - - 2 - 1 0 - - - - 0

(13)

13



 

false j true

i

a[ , ] (頂点iからjへの道が存在する場合)

(頂点iからjへの道が存在しない場合)

グラフの推移的閉包

(275ページ)

グラフの推移的閉包とは、次のような行列aのことである。

Floydのアルゴリズムとほぼ同じで、

相違点は2頂点間の距離を求めるのではなく、

単に道があるかないかだけを調べる。

そのアルゴリズムをWarshallのアルゴリズムと呼ぶ。

(14)

public class Warshall {

public static <E> void search(boolean[][] adjacency, boolean[][] reachable){

for(int i = 0; i < adjacency.length; i++){

reachable[i] = adjacency[i].clone();

}

for(int k = 0; k < adjacency.length; k++){

for(int i = 0; i < adjacency.length; i++){

if( !reachable[i][k] ){

continue;

}

for(int j = 0; j < adjacency[i].length; j++){

reachable[i][j] = reachable[i][j] || reachable[k][j];

} } } } }

14

入力は隣接行列

出力は推移的閉包

(15)

public static void main(String[] args) { System.out.println("

4.5.1");

boolean[][] adjacency = new boolean[6][6];

for(int i = 0; i < adjacency.length; i++){

for(int j = 0; j < adjacency[i].length; j++){

adjacency[i][j] = false;

}

adjacency[i][i] = true;

}

adjacency[0][1] = true;

adjacency[0][2] = true;

adjacency[1][3] = true;

adjacency[2][4] = true;

adjacency[2][5] = true;

adjacency[4][1] = true;

adjacency[4][3] = true;

boolean[][] reachable = new boolean[adjacency.length][];

search(adjacency, reachable);

System.out.println("

推移的閉包

");

for(int i = 0; i < reachable.length; i++){

for(int j = 0; j < reachable[i].length; j++){

if(reachable[i][j]){

System.out.print("+ ");

} else {

System.out.print("- ");

} }

System.out.println();

}

} 15

4.5.1

推移的閉包

+ + + + + + - + - + - -

- + + + + + - - - + - -

- + - + + - - - - +

(16)

期末試験

• 教室 : C101 (いつものA107は使えませ ん)

• 日時 : 2012年7月30日16時30分~18 時00分

– 入室限度 : 16時50分まで – 退出可能 : 17時00分より

• 持ち込み可

– 教科書・資料 ( 自筆・コピー問わず ) は持ち込み可 – 人間・パソコン・携帯電話・ PHS など持ち込み不可

• 学生証必携

– 持っていない場合は教務で発行してもらうこと

16

参照

関連したドキュメント

現実感のもてる問題場面からスタートし,問題 場面を自らの考えや表現を用いて表し,教師の

[r]

[r]

最大消滅部分空間問題 MVSP Maximum Vanishing Subspace Problem.. MVSP:

〔問4〕通勤経路が二以上ある場合

“Intraday Trading in the Overnight Federal Funds Market” FRBNY Current Issues in Economics and Finance 11 no.11 (November). Bartolini L., Gudell S.,

国公立大学 私立大学 短期大学 専門学校 就職