アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き c d f e a b 6 3 4 3 2 1...

16
アルゴリズムとデータ構造 2012717酒居敬一([email protected] ) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/2012/index.html 1

Upload: others

Post on 02-Apr-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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

2012年7月17日

酒居敬一([email protected])

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

Page 2: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

最短路の問題 (260ページ)

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

•  対象は辺に重みのついた有向グラフ •  問題は大きく2種類ある

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

2.  すべての2頂点の組み合わせに対して、最短路を求める。 •  1の方法をすべての頂点に適用するのが最善。

2

Page 3: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

図4.5.1 アルゴリズムの動き

B 6

4 3

単一出発点の問題 (261ページ)

•  Dijkstraのアルゴリズム – 各頂点への最短路を出発点に近いところからひとつづつ確定する。

10

6 9

7 4

最小の距離を持つ頂点は確定。 他から来るにしても重みが負の 経路がなければ、距離は減らない。

10

7 4

A 8 8

出発点

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

3

Page 4: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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を使う。

辺の重み

出発点からの距離

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

Page 5: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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から最小の距離をもつ頂点を探索

Page 6: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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

ひとつずつ確定する作戦の前提として、 辺の重みは正の値である。

Page 7: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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

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

ü そこで、集合Uの替わりにヒープを使うことを考える。 ü ただし、リストのようなデータ構造でヒープを実装するのは、  データ操作に必要な時間計算量の多さから不適切である。 ü ヒープとしては頂点への参照を入れた配列を使うことにする。

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

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

7

Page 8: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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])); } } } } 8

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

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

Page 9: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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のアルゴリズムなので、 ひとつずつ確定する作戦の前提として、 辺の重みは正の値である。

Page 10: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

全頂点間の問題 (270ページ)

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

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

10

Page 11: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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のアルゴリズム

Page 12: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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

Page 13: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

13

⎩⎨⎧

=falsetrue

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

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

グラフの推移的閉包 (275ページ)

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

Floydのアルゴリズムとほぼ同じで、 相違点は2頂点間の距離を求めるのではなく、 単に道があるかないかだけを調べる。 そのアルゴリズムをWarshallのアルゴリズムと呼ぶ。

Page 14: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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

入力は隣接行列

出力は推移的閉包

Page 15: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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 推移的閉包 + + + + + + - + - + - - - + + + + + - - - + - - - + - + + - - - - - - +

Page 16: アルゴリズムとデータ構造...図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 3 2 1 6 単一出発点の問題 (261ページ) • Dijkstraのアルゴリズム

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

•  日時: 2012年7月30日16時30分~18時00分 – 入室限度: 16時50分まで – 退出可能: 17時00分より

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

•  学生証必携 – 持っていない場合は教務で発行してもらうこと

16