アルゴリズムと データ構造 第8回 探索 前回の復習 キュー 抽象データ型としてのキュー 単方向リストによるキューの実現 循環配列(リングバッファ)によるキューの実現 前回の演習問題の答え 問題1: 次のような一連の操作を実行した後のキューQの内容を 示しなさい。 Enq(Q,a), Enq(Q,b), Enq(Q,c), Deq(Q), Deq(Q), Enq(Q,d), Deq(Q), Enq(Q,e). 練習問題 ① Enq(Q,a) ② Enq(Q,b) de abc ③ Enq(Q,c) ④ Deq(Q) ⑤ Deq(Q) c ⑥ Enq(Q,d) b ⑦ Deq(Q) a ⑧ Enq(Q,e) Q 前回の演習問題の答え 問題2: アルゴリズムenqueue(Q,x)をプログラムとして実現しなさい。 Enqueue(Q, x){ if (number < max){ number ← number+1; tail ← (tail mod max) + 1; Q[tail] ← x; } else{ print(“Queue Q over flows.”) } } 前回の演習問題の答え #include <stdio.h> #define max 256 /*キューの最大サイズ*/ struct queue /*配列(構造体)によるキュー*/ { int number; int tail; char element[max]; /*キューに保存するデータは文字に限る*/ }; void enqueue(struct queue *Q, char x){ if (Q->number < max){ Q->number = Q->number+1; Q->tail = (Q->tail mod max) + 1; Q->element[Q->tail] = x; } else{ print(“Queue Q over flows.”); } } 探索 探索とは データの集合から、目的とする値を持った要素を 探し出すこと 探し出す値の項目:キー データの一部 キーの指定方法はさまざま 一致、範囲指定、近接など 探索失敗する(見つからない)こともあり 探索 探索アルゴリズム 用途、目的、実行速度、対象のデータ構造などに より使い分け 探索以外にどのような操作を行うかも考慮 単に見つかればOK→計算時間が短ければよい データの追加や削除もあり→追加や削除のコストがあ まり大きくならないように 線形探索 線形探索(逐次探索)とは 目的とするキー値と一致するまでデータを先頭か ら順に探索 探索の終了 探索すべき値と等しい要素があった場合 探索すべき値と等しい要素がなく末端を通り越した場合 探索のコスト n項目あれば平均n/2回 線形探索 7を探索 6 4 3 7 1 9 8 1 9 8 探索成功 5を探索 6 4 3 7 探索失敗 線形探索 線形探索プログラム(無限ループ版) int search(const int a[], int n, int key) { int i = 0; while (1) { if (i == n) return (-1); if (a[i] == key) return (i); i++; } } /* 無限ループ */ /* 探索失敗 */ /* 探索成功 */ /* 次の要素 */ 線形探索 線形探索プログラム(for版) int search(const int a[], int n, int key) { int i; for (i = 0; i < n; i++) if (a[i] == key) return (i); return (-1); } /* 探索成功 */ /* 探索失敗 */ 線形探索 番兵法 データの末尾まで探索したかのチェックを省略 末尾にキー値を格納:番兵 データ内に一致する値がなくても必ず番兵と一致 一致位置が末尾なら探索失敗と判断 番兵法 7を探索 6 4 3 7 1 9 8 番兵 探索成功 5を探索 6 4 3 7 7 1 9 8 5 探索失敗 線形探索 番兵法による線形探索(無限ループ版) int search(const int a[], int n, int key) { int i = 0; a[n] = key; /* 末尾に番兵を配置 */ while (1) { /* 無限ループ */ if (a[i] == key) break; /* 探索成功 */ i++; /* 次の要素 */ } return (i == n ? -1 : i); /* 末尾なら探索失敗 */ } 線形探索 番兵法による線形探索(for版) int search(const int a[], int n, int key) { int i; a[n] = key; /* 末尾に番兵を配置 */ for (i = 0; ; i++) /* ループ終了条件不要 */ if (a[i] == key) break; /* 探索成功 */ return (i == n ? -1 : i); /* 末尾なら探索失敗 */ } 2分探索 (binary search) 2分探索とは 要素があらかじめソート(整列)されているデータ から効率よく探索を行うアルゴリズム 中央の値に着目し、その値より大きいか小さいか で探索範囲を半分に絞り込み 探索のコスト n項目あれば平均log(2)n回 2分探索 39を探索 5 7 15 28 29 31 39 58 < 39探索成功 6を探索 5 7 15 < 6 探索失敗 >6 >6 28 29 31 >6 39 58 68 70 95 > 39 68 70 95 2分探索 探索アルゴリズム • 探索範囲の先頭の添字をpl、末尾の添字をpr、中央の添字をpcと表すことにする。 • 探索開始時におけるplは0、prはn-1、pcは(n-1)/2である。 Key=a[pc]のとき 探索成功 Key > a[pc]のとき 探索範囲をa[pc+1] ~ a[pr]に絞り込むことができる。plの値をpc+1に更新する。 Key < a[pc]のとき 探索範囲をa[pl] ~ a[pc-1]に絞り込むことができる。prの値をpc-1に更新する。 アルゴリズムの終了条件は、以下に示す条件(c1)と(c2)のいずれかがせいりつすることである。 (c1) a[pc]とKeyが一致した (c2) 探索範囲がなくなった 2分探索 pl 39を探索 5 7 15 28 29 pc plplpc pr pc 31 68 39 58 < 39探索成功 pl pc plpr pl pc plpc pr 6を探索 5 7 15 < 6 探索失敗 >6 >6 28 pr pc 29 31 >6 pr 70 95 > 39 pr 39 58 68 70 95 ハッシュ法 ソート済み配列の操作 データの追加:格納位置からあとの配列をすべて 1つずつ移動し、格納位置をあける必要あり データの削除:削除位置からあとの配列をすべて 1つずつ移動し、削除位置を埋める必要あり ハッシュ法とは 格納する値を元に、簡単な計算で格納位置を求 め、効率よく格納、探索を行う方法 ハッシュ法(hashing) 特徴 ハッシュ法では,検索キーの値を,データが格納される位置(配列の添字) に直接関連付ける これにより,データの個数によらず,データの探索が一定時間で行える 計算量 O (1) (一定時間) 用語 ハッシュ関数(hash function) h(x) : キーの値x を配列の添字 へ写像する関数 ハッシュ値(hash value) : ハッシュ関数h(x)が返す値 ハッシュ表(hash table) : データを格納する配列 バケット(bucket) : ハッシュ表の各要素 ハッシュ法の概要 ハッシュ法による探索 キー”apple” は どこ? x=“apple” h(x) ハッシュ関数 4 (ハッシュ値) Table[4]にアクセス ハッシュテーブル banana kiwi apple orange mango table[0] table[1] table[2] table[3] table[4] table[5] table[6] table[7] 線形探索 キー”apple” は どこ? 違う banana 違う orange 違う mango 見つけた! apple 先頭から順番に探していく必要がある kiwi ハッシュ関数の例 int hashfunc(char *s) { int i = 0; while (*s) i += *s++; return i % 100; } 文字列 a p p l e \0 文字 コード 97 112 112 108 101 0 各文字の文字コードを 足しこむ 530 % 100 = 100の剰余をとることで, ハッシュ値が0~99の範囲 に収まる s ハッシュ値 30 文字コード ASCIIコード American Standard Code for Information Interchange 1文字1byte(8bit)で表現 char型が表現できる範囲 -128 ~ 0 ~ +127 128種類 6×16+1=97 ハッシュ法 ハッシュ法の原理 キー値からハッシュ値(配列の場合は添え字)を 求める:ハッシュ関数 キー値 5 6 14 20 29 34 37 51 69 75 13で割った剰余 5 6 1 7 3 8 11 12 4 10 5 6 7 8 9 10 11 12 5 6 20 34 - 75 37 51 0 1 2 3 4 - 14 - 29 69 ハッシュ法 ハッシュ法による要素の挿入 0 1 2 3 4 - 14 - 29 69 5 6 7 8 9 10 11 12 5 6 20 34 - 75 37 51 5 6 7 8 9 10 11 12 5 6 20 34 35 75 37 51 35を挿入:35=13x2+9 ハッシュ値9 添え字9の位置に挿入 0 1 2 3 4 - 14 - 29 69 衝突(コリジョン, collision) 異なるキーに対して同一のハッシュ値が生成されるこ とを、コリジョン(衝突)という x=“apple” x=“almond” h(x) ハッシュ関数 4 (ハッシュ値) 衝突 4 (ハッシュ値) コリジョンが発生した場合の対策には以下の2通りの 方法がある チェイン法 (chaining) = 外部ハッシュ法 同一ハッシュ値を持つ要素を線形リストで管理 オープンアドレス法 (open addressing) = 内部ハッシュ法 空きバケットが見つかるまでハッシュの繰り返し 注意:用語の別名 D. E. Knuth, “The Art of Computer Programming,” Vol. 3:Sorting and Searching, Addison-Wesley, 1973 チェイン法(chaining) オープンアドレス法(open addressing) N. Wirth, “Algorithms + Data Structures = Programs,” Prentice Hall, 1976 この呼び方を使用 こちらの呼び名を 使っている本の方が多い 直接チェイン法(direct chaining) オープンアドレス法(open addressing) A. V. Aho, J. E. Hopcroft, J. D. Ullman, “Data Structures and Algorithms,” Addison-Wesley, 1983 オープンハッシュ法(open hashing) クローズドハッシュ法(closed hashing) オープンが 逆なので注意!!!! チェイン法(chaining) 同一のハッシュ値を持つデータを連結リストで処理 例1: 0 1 13 14 2 3 4 5 6 7 8 29 69 17 5 6 33 20 46 34 69 17を挿入 33を挿入 46を挿入 33を削除 ハッシュ値7 ハッシュ値4 20 9 10 11 12 75 37 51 チェイン法(chaining) 例2: データの探索 orange 見つけた! 違う ハッシュ値 0 table[0] kiwi orange table[1] table[2] mango table[3] table[4] table[5] almond apple banana チェイン法の解析 データの個数Nに対してバケット数Bが十分大きい場合 • 一定時間 O(1) で探索可能 • 使用されないバケットが発生する可能性あり(メモリの無駄) table[0] table[1] table[2] データの個数Nに対してバケット数Bが小さい場合 • 探索に時間がかかる ( 最悪でO(N) ) • 空きバケットは減少 table[3] table[4] table[0] table[5] table[6] table[7] table[8] table[9] あらかじめデータの個数を見積もり, それに見合った数のバケットを用意する ハッシュ関数の効率 効率のよいハッシュ関数 ⇒データが各バケットに一様 に割当てられる 効率のよくないハッシュ関数 ⇒データが特定のバケットに偏る table[0] table[0] table[1] table[1] table[2] table[2] table[3] table[3] table[4] table[4] table[5] table[5] オープンアドレス法(open addressing) 衝突が起きたら再ハッシュ ハッシュ関数のほかに再ハッシュ関数 空きバケットを求め順にたぐる→線形探査法 衝突をランダムに処理する方法 要素削除のときの問題 すでに削除されたのと同じ要素が再ハッシュで配置さ れていた場合、たどり着けない→削除済みフラグ 方法1.再ハッシュ(rehashing) すでにバケットが使用されていた場合,空いている別のバ ケットにデータを保存 1次ハッシュ B = 8 の場合 lemon hi ( x) (h( x) i ) mod B i : 再ハッシュの回数 バケットがふさがっていた 場合,循環的に直後のバ ケットを調べることになる ⇒ふさがったバケットが並び がちになる h(x) = 0 table[0] grape table[1] banana table[2] kiwi table[3] h1(x) = 1 h2(x) = 2 h3(x) = 3 再ハッシュ(rehashing) 例3: 0 1 2 3 4 - 14 - 29 69 18を挿入 5 6 7 8 9 10 11 12 5 6 18 - 34 - 75 37 51 18 18 18 衝突 衝突 再ハッシュ値7:添え字7の位置に挿入 ハッシュ値5:添え字5の位置に挿入 再ハッシュ値6:添え字6の位置に挿入 再ハッシュ(rehashing) 例4: データの 格納 table[0] table[1] banana almond table[2] kiwi table[3] ハッシュ値 4 衝突 table[4] apple 再ハッシュ table[5] almond バケット数8の ハッシュテーブル table[6] orange table[7] ハッシュ値 5 空いてる 再ハッシュ(rehashing) 例4: データの 探索 table[0] table[1] banana almond table[2] kiwi table[3] ハッシュ値 4 違う table[4] apple 再ハッシュ table[5] almond table[6] orange table[7] 見つけた! ハッシュ値 5 方法2:衝突をランダムに処理する方法 1からB-1までの数字が必ず1回だけランダムに出 現する数列d1, d2, … ,dB-1を用い, 次式により求める hi ( x) (h( x) di ) modB 例)数列{ 5, 1, 2, 4, 3, 6, 7}を用いた場合(B=8) キーh(x)=3のとき 0, 4, 5, 7, 6, 1, 2 3以外のバケットを ランダムな順番で 調べられる ランダムな数列をどうやって作るかがポイント ⇒シフトレジスタを利用するなど シフトレジスタによる乱数列の生成 B : 2のべき乗の数 (例: 21,22,23, …) k : 1~B-1までの間のある定数 di : 最初の値 1~B-1の乱数をうまく生成できるk をあら かじめ見つけておく必要がある ① diを2倍 (1ビット左シフト) ② 結果がBを超えていたら,Bを減算し,減算 結果と,あらかじめ決めておいたkとの排他 的論理和をとる 乱数が得られる 乱数列の生成例 d1 k B=8(=23 ), k=3, di=5の場合 1~7の乱数が 得られた d2 d3 d4 k d5 d6 k=5でもうまくいくが, その他のkではうまくいかない k d7 1 1 0 0 0 0 1 1 0 0 0 1 1 0 1 0 0 1 1 0 1 1 0 0 0 1 =5 <<1 0 1 1 1 0 1 1 0 1 1 0 0 1 =3 =6 1 1 1 =7 =1 =2 =4 41 空のバケットの区別 データが削除されたのか,使われたことがないのか を区別しない場合 table[0] 別のバケットに入っ ているかもしれない … table[1] banana table[2] kiwi table[3] table[4] apple table[5] table[6] orange table[7] almond lemon ハッシュ値 1 再ハッシュ ハッシュ値 3 再ハッシュ ハッシュ値 5 再ハッシュ 空のバケットの区別(deletedとempty) データが削除された (deleted)のか,使われたことが ないのか(empty)を区別することで,emptyのバケット を見つけた時点で探索を打ち切れる 0: empty -1: deleted lemon table[0] ハッシュ値 1 table[1] banana table[2] kiwi table[3] 再ハッシュ ハッシュ値 3 -1 table[4] apple table[5] 再ハッシュ ハッシュ値 5 0 table[6] orange table[7] almond 探索終了 オープンアドレス法の解析 探索時に調べるバケット数(探索1回あたりの平均) 再ハッシュ時に 順番に バケットを調べる場合 再ハッシュ時に ランダムな数によって バケットを調べる場合 データが見つかった 場合 (探索成功) 1 / 2 1 1 log e 1 データが見つからな い場合 (探索失敗) 1 1 2 2(1 2 ) 1 1 αはハッシュ表の使用率 登録したデータ数 全バケット数 1 探索成功時の比較バケット数 ハッシュ表の 使用率 α 再ハッシュ時に 順番に バケットを調べる場合 再ハッシュ時に ランダムな数によって バケットを調べる場合 80% 3.0個 2.0個 90% 5.5個 2.6個 ハッシュ法の比較 チェイン法 オープンアドレス法 = バケット数B 登録データ数 N の上限 なし 操作の効率 N ≧B のとき急速に N が B に近づくにつ データ数N の目安 (ただし,データ数が多すぎる と探索時間が長くなる) 低下 れ急速に低下. 2B ≧ N 0.9B ≧ N まとめ 探索 線形探索 番兵による線形探索 2分探索 ハッシュ法 ハッシュ法によるデータの格納・探索 衝突を処理する方法 チェイン法 再ハッシュ(rehashing) オープンアドレス法 ランダムに処理する方法 演習問題 バケット数5のハッシュ表を考える.ハッシュ関数 h(x)= x mod 5 を用いて,空のハッシュ表に数値データ x ={23, 48, 35, 4, 10}を格納する場合について以 下の問に答えよ. (問1) チェイン法を用いてデータを格納した場合,ハッシュ表はどうなるか? 図を描け. (問2) オープンアドレス法(再ハッシュ)を用いてデータを格納した場合,ハッ シュ表はどうなるか?図を描け. 名前と学籍番号をご記入のうえ、解答用紙(A4)を提出する 提出先: 工学部電子情報実験研究棟5階 NO.5506室のドアのポストに入れてください 締め切り時間: 来週月曜日(6月9日) 午前9時まで
© Copyright 2025 ExpyDoc