酒居敬一(sakai.keiichi@kochi-tech.ac.jp) アルゴリズムとデータ構造 2011年7月11日 酒居敬一(sakai.keiichi@kochi-tech.ac.jp) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/2011/index.html.

Slides:



Advertisements
Similar presentations
山元進.  for 文  while 文  do ~ while 文  文のネスト  break 文  continue 文.
Advertisements

アルゴリズムとデータ構造 2011年7月7日
2009/12/4 グラフ (2) 第10講: 平成21年12月4日 (金) 4限 E252教室 コンピュータアルゴリズム.
アルゴリズムとデータ構造 2012年6月27日
アルゴリズムとデータ構造1 2008年7月22日
アルゴリズムとデータ構造 2013年6月18日
アルゴリズムとデータ構造1 2005年7月8日
アルゴリズムとデータ構造 2010年7月5日
アルゴリズムとデータ構造 2012年7月26日
アルゴリズムとデータ構造 2012年7月19日
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
アルゴリズムとデータ構造 2012年6月14日
アルゴリズムとデータ構造 2011年6月13日
第11講: 平成18年12月 8日 (金) 4限 E352教室 グラフ (1).
大岩 元 慶応大学環境情報学部 二分木 データ構造とプログラミング(10) 大岩 元 慶応大学環境情報学部
アルゴリズムとデータ構造 2011年6月14日
アルゴリズムとデータ構造 2011年6月20日
最短経路.
アルゴリズム入門.
アルゴリズムとデータ構造 2013年7月18日
アルゴリズムとデータ構造 2012年7月12日
アルゴリズムとデータ構造 2011年7月4日
アルゴリズムとデータ構造 2012年6月28日
アルゴリズムとデータ構造 2011年6月27日
第9章 例外処理,パッケージ 9.1 例外処理 9.2 ガーベッジコレクション.
第25章 単一始点最短路 3節 Bellman-Fordのアルゴリズム
アルゴリズムとデータ構造 2013年7月16日
グラフアルゴリズムの可視化 数理科学コース 福永研究室 高橋 優子 2018/12/29.
アルゴリズムとデータ構造1 2006年7月4日
アルゴリズムとデータ構造1 2005年7月15日
アルゴリズムとプログラミング (Algorithms and Programming)
コンパイラ 2012年11月15日
オブジェクト指向 プログラミング 第七回 知能情報学部 新田直也.
アルゴリズムとデータ構造1 2005年7月1日
アルゴリズムとデータ構造1 2009年6月29日
アルゴリズムとデータ構造 2011年7月12日
アルゴリズムとデータ構造 2012年7月17日
アルゴリズムとデータ構造1 2009年7月9日
アルゴリズムとデータ構造1 2005年7月5日
アルゴリズムとデータ構造1 2005年6月24日
アルゴリズムとデータ構造 2010年6月21日
アルゴリズムとデータ構造 2010年7月26日
アルゴリズムとデータ構造 2011年7月21日
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
アルゴリズムとデータ構造1 2006年7月11日
アルゴリズムとデータ構造 2011年6月23日
情報知能学科「アルゴリズムとデータ構造」
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
アルゴリズムとデータ構造1 2006年7月4日
アルゴリズムとデータ構造 2012年7月2日
アルゴリズムとデータ構造 2012年6月11日
アルゴリズムとプログラミング (Algorithms and Programming)
アルゴリズムとデータ構造1 2008年7月24日
アルゴリズムとデータ構造 2011年6月28日
アルゴリズムとデータ構造1 2006年6月23日
アルゴリズムとデータ構造 2013年7月1日
アルゴリズムとデータ構造1 2009年7月2日
アルゴリズムとデータ構造 2013年7月2日
アルゴリズムとデータ構造 2013年7月8日
アルゴリズムとデータ構造1 2009年6月15日
2009/11/27 グラフ (1) 第9講: 平成21年11月27日 (金) 4限 E252教室 コンピュータアルゴリズム.
アルゴリズムとデータ構造 2012年6月25日
アルゴリズムとデータ構造 2012年6月21日
アルゴリズムとデータ構造 2010年6月17日
アルゴリズムとデータ構造1 2005年7月12日
アルゴリズムとデータ構造1 2007年7月6日
オブジェクト指向 プログラミング 第六回 知能情報学部 新田直也.
ねらい 数値積分を例題に、擬似コードのアルゴリズムをプログラムにする。
計算機プログラミングI 第5回 2002年11月7日(木) 配列: 沢山のデータをまとめたデータ どんなものか どうやって使うのか
アルゴリズムとデータ構造 2012年7月9日
Presentation transcript:

酒居敬一(sakai.keiichi@kochi-tech.ac.jp) アルゴリズムとデータ構造 2011年7月11日 酒居敬一(sakai.keiichi@kochi-tech.ac.jp) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/2011/index.html

最短路の問題 (260ページ) 最短路問題 対象は辺に重みのついた有向グラフ 問題は大きく2種類ある グラフの2頂点間を結ぶ道のうちで辺の重みの和が最小のものを求める問題。 対象は辺に重みのついた有向グラフ 問題は大きく2種類ある 出発点を1つに固定して、そこから他のすべての頂点への最短路を求める。 すべての2頂点の組み合わせに対して、最短路を求める。 1の方法をすべての頂点に適用するのが最善。

単一出発点の問題 (261ページ) Dijkstraのアルゴリズム 各頂点への最短路を出発点に近いところからひとつづつ確定する。 C D F 確定した頂点から行くことのできる 頂点への距離を計算。 図4.5.1 アルゴリズムの動き C D F E A B 6 3 4 2 1 6 6 8 9 8 出発点 A 7 7 最小の距離を持つ頂点は確定。 他から来るにしても重みが負の 経路がなければ、距離は減らない。 4 4 10 10

本日使う頂点のデータ構造 辺に重みを持たせるため、Mapを使う。 出発点からの距離 辺の重み 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); // 有向辺を追加 辺に重みを持たせるため、Mapを使う。 出発点からの距離 辺の重み 本日使う頂点のデータ構造

教科書263ページの擬似プログラムをJavaで実装 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())); 集合V 集合U 集合Uから最小の距離をもつ頂点を探索 Javaだと楽勝。 教科書263ページの擬似プログラムをJavaで実装

ひとつずつ確定する作戦の前提として、 辺の重みは正の値である。 図4.5.1 頂点Aからの距離 頂点A: 0 頂点B: 6 頂点C: 4 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() + "からの距離"); System.out.println("頂点" + node.getValue() + ": " + node.getDistance()); ひとつずつ確定する作戦の前提として、 辺の重みは正の値である。 図4.5.1 頂点Aからの距離 頂点A: 0 頂点B: 6 頂点C: 4 頂点D: 8 頂点E: 7 頂点F: 10

263ページの実装では、集合Uから最小の距離をもつ 頂点を線形探索し、集合Uから移動していた。 そもそも、線形探索は時間計算量が多い。 263ページの実装では、集合Uから最小の距離をもつ  頂点を線形探索し、集合Uから移動していた。 そもそも、線形探索は時間計算量が多い。 そこで、集合Uの替わりにヒープを使うことを考える。 ただし、リストのようなデータ構造でヒープを実装するのは、  データ操作に必要な時間計算量の多さから不適切である。 ヒープとしては頂点への参照を入れた配列を使うことにする。 最小の距離を持つ頂点をヒープから移動し、ヒープを再構成する。 実装は省略しますが、263ページの擬似プログラムが単純なので、 それを理解してみるといいだろう。それから267ページを理解する。 というか、集合の実装にHashSetは不適切だとしても、ヒープを自前で実装するのもアレなので、もっと便利なクラスを使うべきです。何を使えばいいでしょうか? ヒープの復習のために自前で実装するのはアリですが、すでに学習済みならDijkstraアルゴリズムの学習のために楽してもいいでしょう。

265ページのプログラムのJava実装 頂点の配列と、頂点同士の接続行列 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); if(weights[p][x] == Integer.MAX_VALUE){ continue; nodes[x].setDistance( Math.min(nodes[x].getDistance(), nodes[p].getDistance() + weights[p][x])); 頂点の配列と、頂点同士の接続行列 265ページのプログラムのJava実装

これも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() + "からの距離"); System.out.println("頂点" + node.getValue() + ": " + node.getDistance()); 図4.5.1 頂点Aからの距離 頂点A: 0 頂点B: 6 頂点C: 4 頂点D: 8 頂点E: 7 頂点F: 10 これもDijkstraのアルゴリズムなので、 ひとつずつ確定する作戦の前提として、 辺の重みは正の値である。

全頂点間の問題 (270ページ) 単一の出発点のアルゴリズムを、全頂点に 順に適用する方法がひとつ。 重み行列が与えられるとき、Floydの アルゴリズムを使って全頂点間の距離を 求める方法もがある。

頂点kを経由して、頂点iから頂点jに至る道があれば、 距離をあらわす行列にその距離を計算し格納する。 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++){ 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){ int w = distances[i][k] + distances[k][j]; if(w < distances[i][j]){ distances[i][j] = w; 頂点kを経由して、頂点iから頂点jに至る道があれば、 距離をあらわす行列にその距離を計算し格納する。 270ページのプログラム Floydのアルゴリズム

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(); 図4.5.1 頂点間の距離 0 6 4 8 7 10 - 0 - 3 - - - 5 0 4 3 6 - - - 0 - - - 2 - 1 0 - - - - - - 0

グラフの推移的閉包とは、次のような行列aのことである。 (275ページ) グラフの推移的閉包とは、次のような行列aのことである。 (頂点iからjへの道が存在する場合) (頂点iからjへの道が存在しない場合) Floydのアルゴリズムとほぼ同じで、 相違点は2頂点間の距離を求めるのではなく、 単に道があるかないかだけを調べる。 そのアルゴリズムをWarshallのアルゴリズムと呼ぶ。

入力は隣接行列 出力は推移的閉包 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++){ if( !reachable[i][k] ){ continue; for(int j = 0; j < adjacency[i].length; j++){ reachable[i][j] = reachable[i][j] || reachable[k][j]; 入力は隣接行列 出力は推移的閉包

図4.5.1 推移的閉包 + + + + + + - + - + - - - + + + + + - - - + - - 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(); 図4.5.1 推移的閉包 + + + + + + - + - + - - - + + + + + - - - + - - - + - + + - - - - - - +

期末試験 教室: C101 日時: 2011年7月25日16時30分~18時00分 持ち込み可 学生証必携 入室限度: 16時50分まで 退出可能: 17時00分より 持ち込み可 教科書・資料(自筆・コピー問わず)は持ち込み可 人間・パソコン・携帯電話・PHSなど持ち込み不可 学生証必携 持っていない場合は教務で発行してもらうこと