アルゴリズムとデータ構
造
9.「ハッシュ法」
2011 年 4 月 26 日(火)
服部 健太
2011/4/26 アルゴリズムとデータ構造 9 2
概要
 ハッシュ法の概念について説明する
 ハッシュ法の実現方法について説明する
2011/4/26 アルゴリズムとデータ構造 9 3
直接アドレス表( Direct-address
tables )
 キー空間が小さければ,キーそのものをインデック
スにして,配列にデータを格納すれば, O(1) で参
照できる
 例:
 テキスト中の各アルファベットの出現頻度を数える
 アルファベットをキーとしたテーブルに,出現頻度を格納す
る
 アルファベットは 26 文字なので,配列の大きさは 26 でよい
 ‘a’ → 1, ‘b’ → 2, …, ‘z’ → 26 とする
 def count(T, x):
 T[x] = T[x] + 1
⇒ キー空間が膨大な場合には,より小さな特定の範囲
に収まるように,キーを変換してやればよい.
2011/4/26 アルゴリズムとデータ構造 9 4
ハッシュ法
 要素の検索や追加が平均 O(1) でできる
 広大なキー空間をハッシュ関数によってある
範囲にマップする
k1
k2 k3
k4
k5
h(k1)
K :実際のキー
U :キー空間全体
h(k4)
h(k2)=h(k5)
h(k3)
ハッシュ表 :T
ハッシュ関数 :h
0
m-1
衝突
( collision
)
2011/4/26 アルゴリズムとデータ構造 9 5
チェイン法
 衝突したレコード同士をリストでつなぐ
ハッシュ表 :T
k1 k4
k5 k2 k7
k3
k8 k6
k1
k2
k3
k4
k5
K :実際のキー
U :キー空間全体
k7
k8
k6
2011/4/26 アルゴリズムとデータ構造 9 6
ハッシュ表の探索(チェイン法)
 ハッシュ表の探索
 入力:ハッシュ表 (HT) ,キー (key)
 出力:見つかったデータを返す(無かったら None )
def search(HT, key):
p = HT.heads[hashfn(key, HT.size)]
while p != None:
if p.key == key:
return p.data
else:
p = p.next
return None
ハッシュ表のインデッ
クスは 0 始まりとする
2011/4/26 アルゴリズムとデータ構造 9 7
ハッシュ表への挿入(チェイン
法)
 ハッシュ表への挿入
 入力:ハッシュ表 (HT) ,キー (key) ,データ
(data)
def insert(HT, key, data):
h = hashfn(key, HT.size)
p = hnode()
p.key = key
p.data = data
p.next = HT.heads[h]
HT.heads[h] = p
2011/4/26 アルゴリズムとデータ構造 9 8
練習問題
 チェイン法の削除手続き DELETE(HT, key) を書け
 キー (key) が見つからない場合はエラーとせよ
def delete(HT, key):
h = hashfn(key, HT.size)
prev = None
p = ???
while p != None and p.key != key:
prev = p
p = p.next
if p == None: error(“key not found”)
if prev == None:
???
else:
???
2011/4/26 アルゴリズムとデータ構造 9 9
解答例
2011/4/26 アルゴリズムとデータ構造 9 10
ハッシュ関数
 望ましいハッシュ関数
 いろいろなキーの値を 0 から m-1 までの範囲にできるだけ一様
に散らばせる(ランダムになる)
 簡単に計算できる
 キーが正整数の場合
 たいていの場合, m で割った余りをとる
 ただし,ハッシュ関数をできるだけランダムなものにするには
, m が素数が望ましい.
 キーが文字列の場合
 何らかの手段で整数値を得て,それを m で割った余りをとる
 長さ k の文字列 s に対して:
 Ord は文字の順序数を得る関数とする
 1 文字を 8 ビットで表す計算機では,定数 c=256(=28
) とすると良い
∑=
−
×=
k
i
ik
misOrdcsHash
1
mod])[()(
2011/4/26 アルゴリズムとデータ構造 9 11
文字列用の簡単なハッシュ関数の例
 入力:文字列 (s) ,ハッシュ表サイズ (m)
 出力:ハッシュ値
def hashfn(s, m):
h = 0
for i in range(0, len(s)):
h = 256 * h + ord(s[i])
return h % m
2011/4/26 アルゴリズムとデータ構造 9 12
チェイン法の計算量
 平均的な計算量は,データ数 N と表(配列の
長さ) M に依存する.
 ハッシュ関数がランダムに近く,値が適度に
バラけることを前提とすると,チェイン法に
おけるリストの長さは,平均 N/M と考えら
れる.
 挿入も探索も N/M に比例する
 N に対して, M を十分大きく取れば, O(1)
2011/4/26 アルゴリズムとデータ構造 9 13
開番地法( open addressing )
 ハッシュ表そのものにレコードを格納
する
 チェイン法では,リストの先頭ポインタ
だった
 レコードの各エントリには,使用中かどう
かを表すフィールドを含める
 衝突時(挿入)の処理
 表の中のハッシュ値が示す位置を調べて,
そこが未使用( FREE )なら,その場所に
書き込む
 使用中( USED )なら,別の場所を調べに
いく.そこが未使用ならその場所に書き込
むし,使用中であれば,また別の場所に移
る
○
○
○
○
使用中かどう
かを表す印.
○は USED の
意
2011/4/26 アルゴリズムとデータ構造 9 14
ハッシュ表への挿入(開番地法)
 ハッシュ表への挿入
 入力:ハッシュ表 (HT) ,キー (key) ,データ (data)
def insert(HT, key, data):
for i in range(0, HT.size):
h = HASH(key, i, HT.size)
if HT.recds[h].mark == ‘FREE’:
HT.recds[h].mark = ‘USED’
HT.recds[h].key = key
HT.recds[h].data = data
return
error(“size over”)
2011/4/26 アルゴリズムとデータ構造 9 15
ハッシュ表の探索(開番地法)
 ハッシュ表の探索
 入力:ハッシュ表 (HT) ,キー (key)
 出力:見つかったデータを返す(無かったら None )
def search(HT, key):
i = 0
while True:
h = HASH(key, i, HT.size)
if HT.recds[h].key == key:
return HT.recds[h].data
i = i + 1
if (HT.recds[h].mark == ‘FREE’
or i == HT.size):
break
return None
2011/4/26 アルゴリズムとデータ構造 9 16
線形走査法( linear probing )
 次の場所を決めるときに,単純にその隣の場
所,さらにその隣,・・・と空きが見つかる
まで,インデックスを1つずつ増やしていく
 配列の最後まで行ったら,先頭に戻る.
def HASH(key, i, size):
return (hashfn(key,size)+i) % size
 クラスタリング現象
 データが固まって存在しやすいため,効率が
良くない
hashfn は通常
のハッシュ関
数
○
○
○
○
+1
+1
hashfn(k)
ここに挿入す
る
2011/4/26 アルゴリズムとデータ構造 9 17
2重ハッシュ法( double
hashing )
 2つのハッシュ関数 h1 と h2 を使う
def HASH(key, i, size):
return (h1(key, size)
+ i * h2(key, size)) % size
def h1(key, size):
return key % size
def h2(key, size):
return 1 + (key % (size – 1))
 h2 は, 1 以上, size-1 以下の値を返す
○
○
○
○
h1(k)
h2(k)
h2(k)
ここに挿入す
る
2011/4/26 アルゴリズムとデータ構造 9 18
ハッシュ表からの削除(開番地
法)
 削除した場所を単純に未使用に戻すと,次に
検索するときに検索の走査がそこで止まって
しまうため,誤った結果になる.
 表の要素の状態に削除済み( DELETED )を加え
, FREE と USED と合わせて3種類とする
 削除するときは,状態を DELETED にしてお
く
 探索のときは,削除ずみの場所があっても次
の場所を見つけに行く
 挿入の際には,削除ずみの場所を再利用して
新しいデータを入れる
2011/4/26 アルゴリズムとデータ構造 9 19
練習問題
 P.18 で説明した方式で,開番地法の探索,
挿入,削除の手続きを書け
 ヒント:
 search, insert は mark に関する条件判定を少し変えれ
ばよい
 この手法の問題点は何か?
 ヒント:挿入と削除を頻繁に繰り返すとどうなる
か?
2011/4/26 アルゴリズムとデータ構造 9 20
解答例

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

  • 1.
  • 2.
    2011/4/26 アルゴリズムとデータ構造 92 概要  ハッシュ法の概念について説明する  ハッシュ法の実現方法について説明する
  • 3.
    2011/4/26 アルゴリズムとデータ構造 93 直接アドレス表( Direct-address tables )  キー空間が小さければ,キーそのものをインデック スにして,配列にデータを格納すれば, O(1) で参 照できる  例:  テキスト中の各アルファベットの出現頻度を数える  アルファベットをキーとしたテーブルに,出現頻度を格納す る  アルファベットは 26 文字なので,配列の大きさは 26 でよい  ‘a’ → 1, ‘b’ → 2, …, ‘z’ → 26 とする  def count(T, x):  T[x] = T[x] + 1 ⇒ キー空間が膨大な場合には,より小さな特定の範囲 に収まるように,キーを変換してやればよい.
  • 4.
    2011/4/26 アルゴリズムとデータ構造 94 ハッシュ法  要素の検索や追加が平均 O(1) でできる  広大なキー空間をハッシュ関数によってある 範囲にマップする k1 k2 k3 k4 k5 h(k1) K :実際のキー U :キー空間全体 h(k4) h(k2)=h(k5) h(k3) ハッシュ表 :T ハッシュ関数 :h 0 m-1 衝突 ( collision )
  • 5.
    2011/4/26 アルゴリズムとデータ構造 95 チェイン法  衝突したレコード同士をリストでつなぐ ハッシュ表 :T k1 k4 k5 k2 k7 k3 k8 k6 k1 k2 k3 k4 k5 K :実際のキー U :キー空間全体 k7 k8 k6
  • 6.
    2011/4/26 アルゴリズムとデータ構造 96 ハッシュ表の探索(チェイン法)  ハッシュ表の探索  入力:ハッシュ表 (HT) ,キー (key)  出力:見つかったデータを返す(無かったら None ) def search(HT, key): p = HT.heads[hashfn(key, HT.size)] while p != None: if p.key == key: return p.data else: p = p.next return None ハッシュ表のインデッ クスは 0 始まりとする
  • 7.
    2011/4/26 アルゴリズムとデータ構造 97 ハッシュ表への挿入(チェイン 法)  ハッシュ表への挿入  入力:ハッシュ表 (HT) ,キー (key) ,データ (data) def insert(HT, key, data): h = hashfn(key, HT.size) p = hnode() p.key = key p.data = data p.next = HT.heads[h] HT.heads[h] = p
  • 8.
    2011/4/26 アルゴリズムとデータ構造 98 練習問題  チェイン法の削除手続き DELETE(HT, key) を書け  キー (key) が見つからない場合はエラーとせよ def delete(HT, key): h = hashfn(key, HT.size) prev = None p = ??? while p != None and p.key != key: prev = p p = p.next if p == None: error(“key not found”) if prev == None: ??? else: ???
  • 9.
  • 10.
    2011/4/26 アルゴリズムとデータ構造 910 ハッシュ関数  望ましいハッシュ関数  いろいろなキーの値を 0 から m-1 までの範囲にできるだけ一様 に散らばせる(ランダムになる)  簡単に計算できる  キーが正整数の場合  たいていの場合, m で割った余りをとる  ただし,ハッシュ関数をできるだけランダムなものにするには , m が素数が望ましい.  キーが文字列の場合  何らかの手段で整数値を得て,それを m で割った余りをとる  長さ k の文字列 s に対して:  Ord は文字の順序数を得る関数とする  1 文字を 8 ビットで表す計算機では,定数 c=256(=28 ) とすると良い ∑= − ×= k i ik misOrdcsHash 1 mod])[()(
  • 11.
    2011/4/26 アルゴリズムとデータ構造 911 文字列用の簡単なハッシュ関数の例  入力:文字列 (s) ,ハッシュ表サイズ (m)  出力:ハッシュ値 def hashfn(s, m): h = 0 for i in range(0, len(s)): h = 256 * h + ord(s[i]) return h % m
  • 12.
    2011/4/26 アルゴリズムとデータ構造 912 チェイン法の計算量  平均的な計算量は,データ数 N と表(配列の 長さ) M に依存する.  ハッシュ関数がランダムに近く,値が適度に バラけることを前提とすると,チェイン法に おけるリストの長さは,平均 N/M と考えら れる.  挿入も探索も N/M に比例する  N に対して, M を十分大きく取れば, O(1)
  • 13.
    2011/4/26 アルゴリズムとデータ構造 913 開番地法( open addressing )  ハッシュ表そのものにレコードを格納 する  チェイン法では,リストの先頭ポインタ だった  レコードの各エントリには,使用中かどう かを表すフィールドを含める  衝突時(挿入)の処理  表の中のハッシュ値が示す位置を調べて, そこが未使用( FREE )なら,その場所に 書き込む  使用中( USED )なら,別の場所を調べに いく.そこが未使用ならその場所に書き込 むし,使用中であれば,また別の場所に移 る ○ ○ ○ ○ 使用中かどう かを表す印. ○は USED の 意
  • 14.
    2011/4/26 アルゴリズムとデータ構造 914 ハッシュ表への挿入(開番地法)  ハッシュ表への挿入  入力:ハッシュ表 (HT) ,キー (key) ,データ (data) def insert(HT, key, data): for i in range(0, HT.size): h = HASH(key, i, HT.size) if HT.recds[h].mark == ‘FREE’: HT.recds[h].mark = ‘USED’ HT.recds[h].key = key HT.recds[h].data = data return error(“size over”)
  • 15.
    2011/4/26 アルゴリズムとデータ構造 915 ハッシュ表の探索(開番地法)  ハッシュ表の探索  入力:ハッシュ表 (HT) ,キー (key)  出力:見つかったデータを返す(無かったら None ) def search(HT, key): i = 0 while True: h = HASH(key, i, HT.size) if HT.recds[h].key == key: return HT.recds[h].data i = i + 1 if (HT.recds[h].mark == ‘FREE’ or i == HT.size): break return None
  • 16.
    2011/4/26 アルゴリズムとデータ構造 916 線形走査法( linear probing )  次の場所を決めるときに,単純にその隣の場 所,さらにその隣,・・・と空きが見つかる まで,インデックスを1つずつ増やしていく  配列の最後まで行ったら,先頭に戻る. def HASH(key, i, size): return (hashfn(key,size)+i) % size  クラスタリング現象  データが固まって存在しやすいため,効率が 良くない hashfn は通常 のハッシュ関 数 ○ ○ ○ ○ +1 +1 hashfn(k) ここに挿入す る
  • 17.
    2011/4/26 アルゴリズムとデータ構造 917 2重ハッシュ法( double hashing )  2つのハッシュ関数 h1 と h2 を使う def HASH(key, i, size): return (h1(key, size) + i * h2(key, size)) % size def h1(key, size): return key % size def h2(key, size): return 1 + (key % (size – 1))  h2 は, 1 以上, size-1 以下の値を返す ○ ○ ○ ○ h1(k) h2(k) h2(k) ここに挿入す る
  • 18.
    2011/4/26 アルゴリズムとデータ構造 918 ハッシュ表からの削除(開番地 法)  削除した場所を単純に未使用に戻すと,次に 検索するときに検索の走査がそこで止まって しまうため,誤った結果になる.  表の要素の状態に削除済み( DELETED )を加え , FREE と USED と合わせて3種類とする  削除するときは,状態を DELETED にしてお く  探索のときは,削除ずみの場所があっても次 の場所を見つけに行く  挿入の際には,削除ずみの場所を再利用して 新しいデータを入れる
  • 19.
    2011/4/26 アルゴリズムとデータ構造 919 練習問題  P.18 で説明した方式で,開番地法の探索, 挿入,削除の手続きを書け  ヒント:  search, insert は mark に関する条件判定を少し変えれ ばよい  この手法の問題点は何か?  ヒント:挿入と削除を頻繁に繰り返すとどうなる か?
  • 20.