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

18
アアアアアアアアアアアア アアアアアアアアアアアア 2013 2013 7 7 18 18 アアアアアア( ([email protected] ) ) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/ 2013/ index.html

Upload: lila-ford

Post on 01-Jan-2016

14 views

Category:

Documents


0 download

DESCRIPTION

アルゴリズムとデータ構造. 2013 年 7 月 18 日 酒居敬一 ( [email protected] ) 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 の内部 - PowerPoint PPT Presentation

TRANSCRIPT

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

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

20132013 年年 77 月月 1818 日日

酒居敬一酒居敬一 (([email protected]))

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

Page 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 文字集合 2

javac に -J-Dfile.encoding=UTF8というオプションを与える理由

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

文字列の照合(298ページ)

3]1[]1[

]1[]1[

][]0[

mpostextmpattern

postextpattern

postextpattern

char[] text = new char[n]; char[] pattern = new char[m];

テキストとパターンの長さをそれぞれn,mとしたとき、それぞれ次のように配列で与えられているとする。(Javaの場合、 charは Unicodeなので、漢字も含まれる。 )

文字列照合あるいは文字列探索とは、テキストとパターンに関して次のような関係の成り立つposを求めることである。

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

public class SimpleMatch { public static int match(char[] text, char[] pattern){ shift: for(int i = 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; }}

4

最後まで一致したら終了

素朴なアルゴリズム(299ページ)

素朴なアルゴリズムでは、テキストの最初から順にパターンと一致する部分があるかどうかを調べていく。

一致しなければ、1文字ずらしてやりなおし

a b ac a b

a b a b

ca cc b

Page 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); }

5

「テキスト内でパターンが見付かったか」「パターン」 6「計算量を気にしなければ、この問題の解法はいとも簡単である。」「テキスト」 -1「 KMP アルゴリズムの比較の回数は、最大 2n 回である。つまり計算量は…」「、最大」 16「 Dijkstra って読むの難しいよね。ダイクストラって発音するんだよ。」「偉い人なんだよ。」 -1「アルゴリズムとデータ構造」「オペレーティングシステム」 -1

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

6

a a a b

a a aa a a

= ≠= ≠== ≠=

a a a ba a a b

素朴なアルゴリズムは時間計算量はO(mn)。実装が簡単なので実行したときの性能はそう悪くない。

一致したという情報を再利用すれば、比較回数が減る。そこで、t文字一致した後に不一致が検出されたとき、パターンをテキストに対してどれだけ進めればいいか、パターンのどこから比較を開始すればいいかを求めておく。

a a a b

a a aa a a

= ≠= ≠== ≠=

a a a ba a a b

テキストとパターンの比較は不一致のあったところからになる。テキストストリームの逆戻りがない。

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

Knuth-Morris-Pratt のアルゴリズム

(301ページ)

• あらかじめパターンを調べておいて不一致が起きたときに、比較回数を減らすべく、次の比較位置を決定する。

• 比較中のテキストの文字位置に戻りがない。• 後述のBMアルゴリズムほどではないが、

素朴なアルゴリズムより実行性能は良い。

7

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

8

a b ac a b

a b a bパターン3文字目で不一致

テキスト

a b a b

ca

a b a b

a b a b

cc b

0 1 0 1

a b a b

-1 0 -1 0

Pascal 的添え字( 教科書の例 )

Java 的添え字

next配列の内容

2文字目で不一致

4文字目で不一致

1文字目で不一致

1文字目で不一致

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

9

パターン

先頭

先頭から

1文字一致

先頭から

1文字一致

先頭から

2文字一致

先頭から全く一致な

し 先頭から全く一致な

し 先頭から全く一致な

し 先頭から全く一致な

し先頭から

1文字一致

先頭から

2文字一致

先頭から

3文字一致

a b ad e a cb a b d f

先頭から全く一致な

-1 0 0 0 -1 1 0 2 -1 0 0 3next配列(Java)

•パターンの中で、パターン先頭から始まる部分文字列が パターン中に現れるかどうかを調べる。•これまで一致していた部分文字列の有無、不一致文字が部分文字列 のどこ含まれているかどうかで操作を決定する。

先頭から

1文字一致

先頭から

2文字一致

a b

-1 0

先頭から

1文字一致

先頭から

2文字一致

a b

2 0

変数t-1 00 00 0011 1 1212 2 3

Page 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 int kmpmatch(char[] text, char[] pattern, int[] next){ int i = 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); }}

パターンは先頭から、テキストは未比較の文字位置からそれぞれ比較するというフラグ。

テキストの中で、パターンと現在比較しているところを指す。 i=0から単調増加である。

(j - 2)文字の一致が見られたときに、パターンを少しずらせて比較を続ける

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

Boyer-Moore のアルゴリズム

(304ページ)

• あらかじめパターンを調べておいて不一致が起きたときに、比較回数を減らすべく、次の比較位置を決定する。– 2つの作戦により、比較回数を減らす。– KMPアルゴリズムでは少なくとも1回は、テ

キストの文字を調べないといけないが、この方法では1回も調べない文字が存在する。その分速い。

• パターンは後ろから比較する。 11

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

12

作戦1

a b c

a b c

a b c

a b c

a b c

a b c

a b c

図5 .1 . 5 作戦1(その1)

図5 .1 . 5 作戦1(その2)

テキスト

パターン

テキスト

パターン

最初の比較で不一致

最初の比較で不一致

比べるだけ無駄

比べるだけ無駄

比べるだけ無駄

新たなるテキストからならば、比べる意味はある

1文字目が一致するので、2文字目以降は比べる意味はある

Page 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); }}

ハッシュテーブルを使うと簡単なので教科書の擬似プログラムを書き換えた。パターンに含まれる文字をキー、スキップ量を値としている。

Page 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); }}

計算の手間はともかく、動作の理解にはいったん元に戻す方法も悪くない。

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

15

作戦2

図5 .1 .10 作戦2(その1)

図5 .1 .11 作戦2(その2)

テキスト

3文字目で不一致

無駄

無駄

a b c a b a b a b

a b c a b

a b c a b

a b c a b

b a b

a b a b

a b a b

a b a b

a b a b

x b

無駄

2文字目で不一致

無駄

無駄

パターンの比較を末尾から行うということを除けば、KMPアルゴリズムと考え方は同じ。

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

16

×

×

×

図5 .1 .12 場合 1

図5 .1 .13 場合2

図5 .1 .14 場合3

文字の並びが同じ部分がある

(ただし、○≠△)

少しずらせる。

文字の並びが同じ部分が少しある

かなりずらせる。

文字の並びが同じ部分がない

Page 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); } }}bmmatchメソッドなどは次のページで…

skipを求める

nextを求める

教科書のm-jに相当するJava表現

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

public static int bmmatch(char[] text, char[] pattern, Map<Character, Integer> skip, int[] next){ 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]){ 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);}