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

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

N/A
N/A
Protected

Academic year: 2021

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

Copied!
18
0
0

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

全文

(1)

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

2012

7

19

酒居敬一

([email protected])

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

1

(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 というオプションを与える理由

(3)

文字列の照合

(298ページ)

]

3

1 [

] 1 [

] 1 [

] 1 [

] [

] 0 [

− +

=

+

=

=

m pos

text m

pattern

pos text

pattern

pos text

pattern

char[] text = new char[n]; 

char[] pattern = new char[m];       

テキストとパターンの長さをそれぞれn,mとしたとき、

それぞれ次のように配列で与えられているとする。

( Java

の場合、

char

Unicode

なので、漢字も含まれる。

)

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

(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文字 ずらしてやりなおし

(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

(6)

6

素朴なアルゴリズムは時間計算量はO(mn)。

実装が簡単なので実行したときの性能はそう悪くない。

一致したという情報を再利用すれば、比較回数が減る。

そこで、t文字一致した後に不一致が検出されたとき、

パターンをテキストに対してどれだけ進めればいいか、

パターンのどこから比較を開始すればいいかを求めておく。

テキストとパターンの 比較は不一致のあった ところからになる。

テキストストリームの 逆戻りがない。

(7)

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


(301ページ)

•  

あらかじめパターンを調べておいて

不一致が起きたときに、比較回数を減らすべく、

次の比較位置を決定する。

•  

比較中のテキストの文字位置に戻りがない。

•  

後述のBMアルゴリズムほどではないが、

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

7

(8)

8

パターン

3文字目で不一致 テキスト

-1 -1

Pascal的添え字

(教科書の例)

Java的添え字

next配列の内容

2文字目で不一致 4文字目で不一致 1文字目で不一致 1文字目で不一致

(9)

9

パターン

-1 0 0 0 -1 1 0 2 -1 0 0 3 next配列

(Java)

• パターンの中で、パターン先頭から始まる部分文字列が  パターン中に現れるかどうかを調べる。

• これまで一致していた部分文字列の有無、不一致文字が部分文字列  のどこ含まれているかどうかで操作を決定する。

-1 0

2 0 -1 0 0 0 0 1 1 2 0 1 2 3 0 1 2 1 変数t

(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)文字の一致が見られたときに、

パターンを少しずらせて比較を続ける

(11)

Boyer-Mooreのアルゴリズム


(304ページ)

•  

あらかじめパターンを調べておいて

不一致が起きたときに、比較回数を減らすべく、

次の比較位置を決定する。

–  

2つの作戦により、比較回数を減らす。

–  

KMPアルゴリズムでは少なくとも1回は、テキスト の文字を調べないといけないが、この方法では 1回も調べない文字が存在する。その分速い。

•  

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

11

(12)

12

作戦1

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

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

テキスト パターン

テキスト パターン

最初の比較で不一致

最初の比較で不一致 比べるだけ無駄

比べるだけ無駄

比べるだけ無駄

新たなるテキストからならば、

比べる意味はある

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)

15

作戦2

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

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

テキスト

3文字目 で不一致

無駄

無駄

無駄 2文字目 で不一致

無駄

無駄 パターンの比較を末尾から行うということを除けば、

KMPアルゴリズムと考え方は同じ。

(16)

16

×

×

×

図5..12 場合1

図5..13 場合2

図5..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);

} }

} bmmatchメソッドなどは次のページで

skipを求める

nextを求める

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

(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);

}

参照

関連したドキュメント

節の構造を取ると主張している。 ( 14b )は T-ing 構文、 ( 14e )は TP 構文である が、 T-en 構文の例はあがっていない。 ( 14a

「文字詞」の定義というわけにはゆかないとこ ろがあるわけである。いま,仮りに上記の如く

いかなる使用の文脈においても「知る」が同じ意味論的値を持つことを認め、(2)によって

従来より論じられることが少なかった財務状況の

噸狂歌の本質に基く視点としては小それが短歌形式をとる韻文であることが第一であるP三十一文字(原則として音節と対応する)を基本としへ内部が五七・五七七という文字(音節)数を持つ定形詩である。そ

P‐ \ovalbox{\tt\small REJECT}根倍の不定性が生じてしまう.この他対数写像を用いた議論 (Step 1) でも 1のp‐ \ovalbox{\tt\small REJECT}根倍の不定性が

Matsui 2006, Text D)が Ch/U 7214

と言っても、事例ごとに意味がかなり異なるのは、子どもの性格が異なることと同じである。その