1 T HE U NIVERSITY OF T OKYO D EPARTMENT OF M ATHEMATICAL I NFORMATICS 数理情報工学演習第一C プログラミング演習 ( 第 6 回 ) 2014/05/19.

Slides:



Advertisements
Similar presentations
【事例演習5】  字句解析     解 説  “ハッシュを用いた字句解析の方法”.
Advertisements

アルゴリズムとデータ構造 第2回 線形リスト(復習).
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
情報処理演習C2 ファイル操作について (2).
第11回 整列 ~ シェルソート,クイックソート ~
数理情報工学演習第一C プログラミング演習 (第3回 ) 2014/04/21
ファーストイヤー・セミナーⅡ 第8回 データの入力.
データ構造とアルゴリズム 第10回 mallocとfree
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
基礎プログラミングおよび演習 第9回
プログラミング演習Ⅱ 第12回 文字列とポインタ(1)
情報工学概論 (アルゴリズムとデータ構造)
第8回 プログラミングⅡ 第8回
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
構造体.
データ構造と アルゴリズム 第八回 知能情報学部 新田直也.
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
データ構造と アルゴリズム 第十一回 理工学部 情報システム工学科 新田直也.
データ構造と アルゴリズム 第四回 知能情報学部 新田直也.
第3回 配列,構造体,ポインタ ~ データ構造について学ぶための基礎~
第13回 ハッシュテーブルを使ったプログラム ~高速に検索するには?~.
算法数理工学 第3回 定兼 邦彦.
ハッシュテーブル.
Cプログラミング演習 中間まとめ2.
データ構造とアルゴリズム 第4回 リスト ~ データ構造(1)~.
プログラミング論 II 2008年10月30日 文字列
Cプログラミング演習 第7回 メモリ内でのデータの配置.
プログラミング 4 記憶の割り付け.
2005年度 データ構造とアルゴリズム 第3回 「C言語の復習:再帰的データ構造」
プログラミング入門2 第11回 情報工学科 篠埜 功.
第7回 プログラミングⅡ 第7回
第11回 プログラミングⅡ 第11回
第9回 優先度つき待ち行列,ヒープ,二分探索木
P n ポインタの基礎 5 q m 5 7 int* p; int 型の変数を指すポインタ int* q; int 型の変数を指すポインタ int n=5, m=7; int 型の変数 int array[3]; int* pArray[3]; p = &n; ポインタにアドレスを代入しているのでOK.
データ構造と アルゴリズム 第五回 知能情報学部 新田直也.
プログラミング基礎B 文字列の扱い.
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
データ構造とアルゴリズム 第11回 リスト構造(1)
文字列へのポインタの配列 static char *lines[MAXLINES]; lines[0] NULL
データ構造とアルゴリズム (第5回) 静岡大学工学部 安藤和敏
第9回 優先度つき待ち行列,ヒープ,二分探索木
高度プログラミング演習 (09).
ポインタとポインタを用いた関数定義.
ファイルの読み込み, ファイルからのデータの取り出し, ファイルの書き出し
算法数理工学 第4回 定兼 邦彦.
情報工学概論 (アルゴリズムとデータ構造)
第11回放送授業.
アルゴリズムとデータ構造1 2009年6月15日
アルゴリズムとデータ構造 補足資料11-3 「線形リストのオペレータ」
ネットワーク・プログラミング Cプログラミングの基礎.
情報処理Ⅱ 第7回 2004年11月16日(火).
コンパイラ 2012年10月11日
プログラミング 4 文字列.
アルゴリズムとデータ構造 2010年6月17日
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
プログラミング言語論 第九回 理工学部 情報システム工学科 新田直也.
プログラミング演習I 2003年6月11日(第9回) 木村巌.
データ構造と アルゴリズム 第四回 知能情報学部 新田直也.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
情報処理Ⅱ 2005年11月25日(金).
プログラミング演習II 2004年11月 16日(第5回) 理学部数学科・木村巌.
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング入門2 第5回 配列 変数宣言、初期化について
TList リスト構造とは? 複数のデータを扱うために、 データの内容と、次のデータへのポインタを持つ構造体を使う。
岩村雅一 知能情報工学演習I 第7回(後半第1回) 岩村雅一
Presentation transcript:

1 T HE U NIVERSITY OF T OKYO D EPARTMENT OF M ATHEMATICAL I NFORMATICS 数理情報工学演習第一C プログラミング演習 ( 第 6 回 ) 2014/05/19

2 T HE U NIVERSITY OF T OKYO 今日の内容 :  文字列  ハッシュ  課題: ハッシュを用いた辞書

3 T HE U NIVERSITY OF T OKYO 文字列 文字列は char の配列 char name[] = “ABC”; –name は “ABC” を格納するのに十分な長さの配列 – 長さは,文字列の長さ + 1 ( 終端記号用 ) char name[4] = {‘A’, ‘B’, ‘C’, 0}; と書くのと同じ C 言語の標準ライブラリでは,文字列の長さは明示的には格納せず, 終端記号 (0) で文字列の終わりを表現する char *name = “ABC”; –name は char のポインタで,メモリ上のどこかに格納された “ABC\0” の アドレスが代入される

4 T HE U NIVERSITY OF T OKYO 文字列処理のライブラリ #include char *strcpy(char *p, char *q); – アドレス p から始まる領域に, q 以降の文字列をコピーする –p 側の領域は, q を格納するために必要なサイズがなければならない (無いとセキュリティホールになる) – つまり, p 側のメモリはあらかじめ確保しておく必要がある char *strcat(char *p, char *q); – 文字列 p の直後の領域に文字列 q をコピーする int strlen(char *s); – 文字列 s の長さを返す.長さは終端記号は含まない.

5 T HE U NIVERSITY OF T OKYO int strcmp(char *p, char *q); – 文字列 p と q を比較して,等しければ 0 , 辞書順で p q ならば正の値を返す if (p == q) とは意味が異なるので注意 –p == q はアドレスの比較をしているだけ –strcmp(p, q) == 0 は,アドレスは違っても中身が同じなら成立 int strcasecmp(char *p, char *q); – 英文字列で,大文字 / 小文字を区別せずに比較 – 環境によっては stricmp という名前の場合もある

6 T HE U NIVERSITY OF T OKYO ファイルから文字列を読み込む方法 ファイル中の文字列の長さは任意であるため,注意しないとメモリを破壊して しまう –char buf[10]; –fscanf(f, “%s”, buf); // 長さが 10 以上の文字列が来るとメモリを破壊 長い文字列が来たときは途中で捨てる char *fgets(char *s, int n,FILE *f); – ファイル f から s の領域に 1 行 ( 最大 n バイト ) 読み込む –char buf[10], p[10], q[10]; –fgets(buf, 10, f);

7 T HE U NIVERSITY OF T OKYO int sscanf(char *s, char *format,...); – 文字列 s から format に従って変数に読み込む –sscanf(buf, “%s %s”, p, q); // buf から文字列を取り出して p, q にコピー –char *p2, *q2; –p2 = malloc(strlen(p)+1); // +1 は終端記号の分 –q2 = malloc(strlen(q)+1); –strcpy(p2, p); –strcpy(q2, q);

8 T HE U NIVERSITY OF T OKYO ハッシュ表 辞書操作 (insert, delete, search) を効率よく実現するデータ構造 –insert(S, x): 集合 S に x を入れる –delete(S, x): 集合 S から x を削除 –search(S, x): 集合 S から x を取り出す ハッシュ表は実際的な場面では極めて速い – 妥当な仮定の下で, SEARCH の時間の期待値は O(1) – リストで辞書を実現すると O(n) – ただし,ハッシュも最悪の場合は  (n)

9 T HE U NIVERSITY OF T OKYO 連想配列 配列の添え字が整数以外のもの –ID[“coffee”] = “ コーヒー ”; ID[“milk”] = “ 牛乳 ”; 実装法 – 配列に入れて線形探索 ( 全部見る ) –key の辞書順にソートして配列に格納し, 2 分探索 –2 分探索木に入れる – ハッシュ ハッシュを使えば効率的に実装できる

10 T HE U NIVERSITY OF T OKYO 直接アドレス表 出現する可能性のあるキーの全集合 ( 普遍集合, universal set) が大きくない場合 にうまく働く キーが普遍集合 U = {0,1, , m  1} から選択され,どの 2 つの要素も同じキーをも たないと仮定する 直接アドレス表 (direct-access table) T で辞書を表現する 配列 T[0..m  1] の各要素が U のキーに対応 T[k] は,キー k を持つ要素をさす.そのような要素がなければ T[k] = NIL T[k] をスロット k と呼ぶ

11 T HE U NIVERSITY OF T OKYO U ( キーの普遍集合 ) K ( 実際 のキー ) キー付属データ T

12 T HE U NIVERSITY OF T OKYO 辞書操作の実現 DIRECT_ADDRESS_SEARCH(T,k) return T[k] DIRECT_ADDRESS_INSERT(T,x) T[key(x)] = x DIRECT_ADDRESS_DELETE(T,x) T[key(x)] = NULL いずれも O(1) 時間 T にオブジェクトそのものを格納してもいい

13 T HE U NIVERSITY OF T OKYO 直接アドレス表の欠点 キーの普遍集合 U が大きい場合は非現実的 表 T をメモリに格納できない T に割り付けた領域のほとんどが無駄になる 辞書に格納されているキーの集合 K が U に比べて十分に小さい場合は ハッシュ表が有効

14 T HE U NIVERSITY OF T OKYO ハッシュ表 直接アドレス表では,キー k はスロット k に格納 ハッシュ表 T[0..m  1] では,スロット h(k) に格納 h: ハッシュ関数 (hash function) h: U  {0,1, ,m  1} 必要な領域 :  (|K|) 要素の探索 : O(1) ( 平均時間 )

15 T HE U NIVERSITY OF T OKYO ハッシュ関数の衝突 衝突 (collision): 2 つのキーが同じスロットにハッシュされること k1k1 U ( キーの普遍集合 ) K ( 実際 のキー ) T k2k2 k3k3 k4k4 h(k2)h(k2) h(k1)h(k1) h(k 3 ) = h(k 4 )

16 T HE U NIVERSITY OF T OKYO 衝突の回避方法 別のハッシュ関数を用いる |U| > m なので完全に回避することは不可能 チェイン法 オープンアドレス法

17 T HE U NIVERSITY OF T OKYO チェイン法による衝突解決 同じスロットにハッシュされたすべての要素を連結リストに格納 hash_insert(T,x) リスト T[h(key(x))] の先頭に x を挿入, O(1) 時間 hash_search(T,k) リスト T[h(k)] の中からキー k を持つ要素を探索 hash_delete(T,x) リスト T[h(key(x))] から x を削除, 双方向リストを用いれば O(1) 時間

18 T HE U NIVERSITY OF T OKYO リストの要素 キーとバリューのペア (key, value) を格納 key に従って要素を検索 key は付属した値 value を持つ hashkey_t と value_t は自分で typedef する typedef struct kvpair_ kvpair; typedef struct dlobj_{ struct dlobj_ *next; struct dlobj_ *prev; kvpair *x; } dlobj; dlist では kvpair の中身の処理はしない typedef struct kvpair_{ hashkey_t key; value_t value; } kvpair;

19 T HE U NIVERSITY OF T OKYO head(L) coffee コーヒー kvpair milk 牛乳 sugar 砂糖 typedef struct dlobj_{ struct dlobj_ *next; struct dlobj_ *prev; kvpair *x; } dlobj; typedef struct kvpair_{ hashkey_t key; value_t value; } kvpair; typedef char *hashkey_t; typedef char *value_t;

20 T HE U NIVERSITY OF T OKYO ハッシュ関数の選び方 h: ハッシュ関数 (hash function) –h: U  {0,1, ,m  1} x  y  h(x)  h(x) になるのが理想だが, |U| > m なら無理 キーが文字列 (typedef char *hashkey_t) のとき,例えば この値を m ( ハッシュ表のサイズ ) で割った余りをハッシュ値とする int hash_func(hashkey_t key) { int h = 0; while (*key != 0) { h = h * tolower(*key++); // 小文字に変換してからハッシュ値を計算 } return abs(h); // 非負の値にする }

21 T HE U NIVERSITY OF T OKYO ハッシュの構造体 typedef struct { int n; // ハッシュに格納されている要素数 int m; // ハッシュ表のサイズ dist **T; // 双方向リストのポインタの配列 ( のポインタ ) } hash;

22 T HE U NIVERSITY OF T OKYO dlist.c では, 2 つの kvpair のキーが等しいかを判定する関数が必要 dlist.c の外部で定義されている関数を使用する int kvpair_cmp(kvpair *x, kvpair *y); // 外部で定義されている関数 dlobj *dlist_search(dlist *L, kvpair *x){ dlobj *now = L->nil->next; while(now != L->nil){ if(kvpair_cmp(now->x, x) == 0){ return now; } now = now->next; } return NULL; }

23 T HE U NIVERSITY OF T OKYO dlist.c で使われる外部関数 int kvpair_cmp(kvpair *x, kvpair *y); –x のキーと y のキーが等しければ 0 を返す –dlist_search() 内で使われる int kvpair_free(kvpair *x); –x のメモリを開放する –dlist_free() 内で使われる dlist.c でこれらの関数のプロトタイプ宣言をしておく

24 T HE U NIVERSITY OF T OKYO hash.c で定義する関数 static int hash_func(hashkey_t key); – ハッシュ値の計算 –hash.c 内部のみで使われる int kvpair_cmp(kvpair *x, kvpair *y); int kvpair_free(kvpair *x); void kvpair_print(kvpair *x); –key と value を表示する

25 T HE U NIVERSITY OF T OKYO hash.h で定義するもの typedef char *hashkey_t; // キーは文字列 typedef char *value_t; // value も文字列 hash の構造体 hash.c で定義される関数のプロトタイプ宣言 typedef struct kvpair_ { hashkey_t key; value_t value; } kvpair; typedef struct { int n; int m; dist **T; } hash;

26 T HE U NIVERSITY OF T OKYO 課題 ( ハッシュを用いた英和辞書 )  hash *hash_new(int m);  表のサイズが m のハッシュ表を作る  dlobj *hash_search(hash *H, hashkey_t key);  キーが key である要素を返す  注 : dlist_search も文字列の比較をするように変更する  dlobj を返す理由は,削除を高速にするため  void hash_insert(hash *H, hashkey_t key, value_t value);  kvpair (key, value) を作り,ハッシュ表に入れる  同じものは H に無いと仮定 (search して無かったときだけ入れる )  void hash_delete(hash *H, dlobj *x);  ハッシュ表から要素 x を削除

27 T HE U NIVERSITY OF T OKYO dlobj には文字列のポインタを格納しているので, ファイルから読み込んだ文字列のメモリ管理に注意 ファイルの各行は – 英単語 日本語訳 5/21( 水 ) の正午までに提出 宛先:

28 T HE U NIVERSITY OF T OKYO 余力のある人は 自由に使える英和辞書データとして なお,見出しと訳はタブ文字 (‘ ∖ t’) で区切られており,見出しは 空白文字を含んでいる scanf の %s で文字列を読むと,空白文字で区切られてしまう sscanf(buf, “%[^ ∖ t] %[^ ∖ n]”, buf1, buf2); とすると, buf1 にはタブの直前までの文字列 ( 空白文字を含む ) , buf2 は行の最後までの文字列が入る 文字列は UNICODE なので注意 –Windows のコマンドプロンプトでは文字化けする –Ubuntu の terminal では UNICODE が標準