1 / 18

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

アルゴリズムとデータ構造. 2013 年 7 月 18 日 酒居敬一 ( sakai.keiichi@kochi-tech.ac.jp ) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/2013/index.html. 文字集合と符号化. 2011 年度までの A-WS 日本語 EUC(EUC-JP) 符号化 JIS X 0208 および ASCII 文字集合 2012 年度からの A-WS UTF-8 符号化 ( 例外 : Mac の javac は sjis) Unicode 文字集合 Java の内部

lila-ford
Download Presentation

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

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

  2. 文字集合と符号化 • 2011年度までのA-WS • 日本語EUC(EUC-JP)符号化 • JIS X 0208およびASCII文字集合 • 2012年度からのA-WS • UTF-8符号化(例外: Macのjavacはsjis) • Unicode文字集合 • Javaの内部 • UTF-16符号化(16bitのcharで保持) • Unicode文字集合 javac に -J-Dfile.encoding=UTF8 というオプションを与える理由

  3. 文字列の照合(298ページ) テキストとパターンの長さをそれぞれn,mとしたとき、 それぞれ次のように配列で与えられているとする。 (Javaの場合、charはUnicodeなので、漢字も含まれる。) char[] text = new char[n]; char[] pattern = new char[m]; 文字列照合あるいは文字列探索とは、テキストとパターンに関して 次のような関係の成り立つposを求めることである。

  4. 素朴なアルゴリズム (299ページ) 素朴なアルゴリズムでは、テキストの最初から順に パターンと一致する部分があるかどうかを調べていく。 a b a b public class SimpleMatch { public static int match(char[] text, char[] pattern){ shift: for(inti = 0; i <= (text.length - pattern.length); i++){ for(int j = 0; j < pattern.length; j++){ if(text[i+j] != pattern[j]){ continue shift; } } return i; } return -1; } } 一致しなければ、1文字ずらしてやりなおし 最後まで一致したら終了 a b c a a b a c c c b

  5. public static void main(String[] args) { String a, b; int c; a = "テキスト内でパターンが見付かったか"; b = "パターン"; c = match(a.toCharArray(), b.toCharArray()); System.out.println("「" + a + "」「" + b + "」 " + c); a = "計算量を気にしなければ、この問題の解法はいとも簡単である。"; b = "テキスト"; c = match(a.toCharArray(), b.toCharArray()); System.out.println("「" + a + "」「" + b + "」 " + c); a = "KMPアルゴリズムの比較の回数は、最大2n回である。つまり計算量は…"; b = "、最大"; c = match(a.toCharArray(), b.toCharArray()); System.out.println("「" + a + "」「" + b + "」 " + c); a = "Dijkstraって読むの難しいよね。ダイクストラって発音するんだよ。"; b = "偉い人なんだよ。"; c = match(a.toCharArray(), b.toCharArray()); System.out.println("「" + a + "」「" + b + "」 " + c); a = "アルゴリズムとデータ構造"; b = "オペレーティングシステム"; c = match(a.toCharArray(), b.toCharArray()); System.out.println("「" + a + "」「" + b + "」 " + c); } 「テキスト内でパターンが見付かったか」「パターン」 6 「計算量を気にしなければ、この問題の解法はいとも簡単である。」「テキスト」 -1 「KMPアルゴリズムの比較の回数は、最大2n回である。つまり計算量は…」「、最大」 16 「Dijkstraって読むの難しいよね。ダイクストラって発音するんだよ。」「偉い人なんだよ。」 -1 「アルゴリズムとデータ構造」「オペレーティングシステム」 -1

  6. 素朴なアルゴリズムは時間計算量はO(mn)。素朴なアルゴリズムは時間計算量はO(mn)。 実装が簡単なので実行したときの性能はそう悪くない。 a a a a a a = = = = = ≠ ≠ ≠ a a a a a a a a a a a a a a a a a a b b b b b b 一致したという情報を再利用すれば、比較回数が減る。 そこで、t文字一致した後に不一致が検出されたとき、 パターンをテキストに対してどれだけ進めればいいか、 パターンのどこから比較を開始すればいいかを求めておく。 a a a a a a テキストとパターンの 比較は不一致のあった ところからになる。 テキストストリームの 逆戻りがない。 = = = = = ≠ ≠ ≠

  7. Knuth-Morris-Prattのアルゴリズム(301ページ)Knuth-Morris-Prattのアルゴリズム(301ページ) • あらかじめパターンを調べておいて不一致が起きたときに、比較回数を減らすべく、次の比較位置を決定する。 • 比較中のテキストの文字位置に戻りがない。 • 後述のBMアルゴリズムほどではないが、素朴なアルゴリズムより実行性能は良い。

  8. b c a a b a c c c b テキスト パターン 3文字目で不一致 a b a b 2文字目で不一致 a a a a b b b b a a a a b b b b 4文字目で不一致 1文字目で不一致 1文字目で不一致 Pascal的添え字 (教科書の例) 0 1 0 1 next配列の内容 -1 0 -1 0 Java的添え字

  9. b d e a a b c a b d f a b a b パターン -1 0 0 0 0 1 1 2 0 1 2 3 0 1 2 1 変数t 先頭 先頭から全く一致なし 先頭から全く一致なし 先頭から全く一致なし 先頭から1文字一致 先頭から1文字一致 先頭から2文字一致 先頭から全く一致なし 先頭から1文字一致 先頭から2文字一致 先頭から3文字一致 先頭から全く一致なし 先頭から1文字一致 先頭から2文字一致 先頭から1文字一致 先頭から2文字一致 next配列 (Java) -1 0 0 0 -1 1 0 2 -1 0 0 3 -1 0 2 0 • パターンの中で、パターン先頭から始まる部分文字列が パターン中に現れるかどうかを調べる。 • これまで一致していた部分文字列の有無、不一致文字が部分文字列 のどこ含まれているかどうかで操作を決定する。

  10. public class KnuthMorrisPratt { private static void kmpinit(char[] pattern, int[] next){ int t = -1; next[0] = -1; for(int j = 1; j < pattern.length; j++){ while((t >= 0) && (pattern[j-1] != pattern[t])) t = next[t]; t++; if(pattern[j] != pattern[t]) next[j] = t; else next[j] = next[t]; } } private static intkmpmatch(char[] text, char[] pattern, int[] next){ inti = 0; int j = 0; while((i < text.length) && (j < pattern.length)){ while((j >= 0) && (text[i] != pattern[j])){ j = next[j]; } i++; j++; } if(j < pattern.length) return -1; return i - j; } public static int match(char[] text, char[] pattern){ int[] next = new int[pattern.length]; kmpinit(pattern, next); return kmpmatch(text, pattern, next); } } パターンは先頭から、 テキストは未比較の文字位置から それぞれ比較するというフラグ。 (j-2)文字の一致が見られたときに、 パターンを少しずらせて比較を続ける テキストの中で、パターンと 現在比較しているところを指す。 i=0から単調増加である。

  11. Boyer-Mooreのアルゴリズム(304ページ) • あらかじめパターンを調べておいて不一致が起きたときに、比較回数を減らすべく、次の比較位置を決定する。 • 2つの作戦により、比較回数を減らす。 • KMPアルゴリズムでは少なくとも1回は、テキストの文字を調べないといけないが、この方法では1回も調べない文字が存在する。その分速い。 • パターンは後ろから比較する。

  12. 作戦1 テキスト a a a a a a a b b b b b b b c c c c c c c パターン 最初の比較で不一致 比べるだけ無駄 a x 比べるだけ無駄 新たなるテキストからならば、 比べる意味はある 図5.1.5 作戦1(その1) テキスト パターン 最初の比較で不一致 比べるだけ無駄 1文字目が一致するので、 2文字目以降は比べる意味はある 図5.1.5 作戦1(その2)

  13. public class BoyerMooreMap { private static void bminit(char[] pattern, Map<Character, Integer> skip){ for(int j = 0; j < pattern.length - 1; j++){ skip.put(pattern[j], pattern.length - j - 1); } } public static int bmmatch(char[] text, char[] pattern, Map<Character, Integer> skip){ shift: for(int i = pattern.length - 1; i < text.length;){ for(int j = pattern.length - 1; j >= 0; i--, j--){ if(text[i] != pattern[j]){ // 教科書のプログラム5.1.8そのまま Integer s = skip.get(text[i]); if(s == null) i += pattern.length; else i += Math.max(s, pattern.length - j); continue shift; } } return ++i; } return -1; } public static int match(char[] text, char[] pattern){ Map<Character, Integer> skip = new HashMap<Character, Integer>(pattern.length*2); bminit(pattern, skip); return bmmatch(text, pattern, skip); } } ハッシュテーブルを使うと簡単なので 教科書の擬似プログラムを書き換えた。 パターンに含まれる文字をキー、 スキップ量を値としている。

  14. public class BoyerMooreMap { private static void bminit(char[] pattern, Map<Character, Integer> skip){ for(int j = 0; j < pattern.length - 1; j++){ skip.put(pattern[j], pattern.length - j - 1); } } public static int bmmatch(char[] text, char[] pattern, Map<Character, Integer> skip){ shift: for(int i = pattern.length - 1; i < text.length;){ for(int j = pattern.length - 1; j >= 0; i--, j--){ if(text[i] != pattern[j]){ // 教科書の309ページにあるようにiを元に戻した場合。 i += pattern.length - 1 - j; Integer s = skip.get(text[i]); i += (s == null)? pattern.length: s; continue shift; } } return ++i; } return -1; } public static int match(char[] text, char[] pattern){ Map<Character, Integer> skip = new HashMap<Character, Integer>(pattern.length*2); bminit(pattern, skip); return bmmatch(text, pattern, skip); } } 計算の手間はともかく、 動作の理解にはいったん元に 戻す方法も悪くない。

  15. 作戦2 パターンの比較を末尾から行うということを除けば、 KMPアルゴリズムと考え方は同じ。 b a b x b a a a a b b b b c c c c a a a a b b b b テキスト a a a a a b b b b b a a a a a b b b b b 3文字目 で不一致 2文字目 で不一致 無駄 無駄 無駄 無駄 無駄 図5.1.10 作戦2(その1) 図5.1.11 作戦2(その2)

  16. × × × 文字の並びが同じ 部分がある (ただし、○≠△) 少しずらせる。 ○ 図5.1.12 場合1 △ 文字の並びが同じ 部分が少しある かなりずらせる。 ○ 図5.1.13 場合2 文字の並びが同じ 部分がない ○ 図5.1.14 場合3

  17. public class BoyerMoore { private static void bminit(char[] pattern, Map<Character, Integer> skip, int[] next){ int[] g = new int[pattern.length]; int j; for(j = 0; j < pattern.length; j++){ next[j] = 2*pattern.length - j - 1; // length + (length - j - 1) } j = pattern.length; for(int k = pattern.length - 1; k >= 0; k--, j--){ g[k] = j; while((j < pattern.length) && (pattern[j] != pattern[k])){ next[j] = Math.min(next[j], pattern.length - k - 1); j = g[j]; } } int s = j; for(j = 0; j < pattern.length; j++){ next[j] = Math.min(next[j], s + pattern.length - j - 1); if(j >= s){ s = g[s]; } } for(j = 0; j < pattern.length - 1; j++){ skip.put(pattern[j], pattern.length - j - 1); } } } nextを求める 教科書のm-jに 相当するJava表現 skipを求める bmmatchメソッドなどは次のページで…

  18. public static intbmmatch(char[] text, char[] pattern, Map<Character, Integer> skip, int[]next){ shift: for(inti = pattern.length - 1; i < text.length;){ for(int j = pattern.length - 1; j >= 0; i--, j--){ if(text[i] != pattern[j]){ Integer s = skip.get(text[i]); if(s == null){ i += Math.max(pattern.length, next[j]); } else { i += Math.max(s, next[j]); } continue shift; } } return ++i; } return -1; } public static int match(char[] text, char[] pattern){ Map<Character, Integer> skip = new HashMap<Character, Integer>(pattern.length*2); int[] next = new int[pattern.length]; bminit(pattern, skip, next); return bmmatch(text, pattern, skip, next); }

More Related