Document

2005年度
データ構造とアルゴリズム
第6回
「ハッシュ法を用いた探索」
西尾 信彦 [email protected]
立命館大学情報理工学部
情報システム学科
O(log n)よりも高速な探索
• 配列のインデックスとキーを一致させれば
O(1)である!!!
• キーの範囲だけの大きさの配列が必要
– 非実用的
• キーから配列のインデックスに変換する適
切な関数を作成,これをハッシュ関数という
– 均等に分配するような関数
– キーを100で割れば大きさは100分の1になる
– 同じハッシュ値となるレコードは同じハッシュバ
ケットに入る
ハッシュ関数
• キーのとりうる範囲を一定の範囲(0~M-1)する
• ハッシュの衝突は極力避けたい
– 均等に分散させるため
• 例えば,整数のキーであれば剰余系を利用する
• 文字列によるキーの場合
– 頭の数文字で生成するのはダメ
– 同じ文字セットの順番が変っていたら違うハッシュ値
が欲しい
– KnuthやWeinbergの例を参照
• ハッシュ関数自体のコストがかかっていたらそも
そもダメ
分離連鎖法
• ハッシュ値の衝突を許容し
– ハッシュバケットをリストで生成する
– 各ハッシュ値のリストの先頭からなる配列を用
意する
– ハッシュ関数でバケットを選択して,リストを逐
次検索
• 計算の手間はN/M?
– 適切なMの選び方
– バケットを構成するリストを工夫する
Hash 分離連鎖法の例:
入力ファイル
Afghanistan
Albania
Algeria
Andorra
Angola
.
.
.
.
Kenya
Kiribati
Korea
実行結果:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HASHSIZE 13
#define STR_MAX 256
typedef struct _listelem{
struct _listelem *next;
char *s;
}listelem, *list;
bucket[0] Jamaica
bucket[0] France
bucket[0] China
.
.
.
bucket[12] Belguim
bucket[12] Argentina
検索ワード:Japan
Japan が見つかりました.
list bucket[HASHSIZE];
char *word;
unsigned long hashpjw(unsigned char* str){
上図のような入力ファイル
を用いて,データ入力を行
い,キーボードから検索
キーを入力し,リストにある
かどうかを調べる
unsigned long hash = 0, g;
for( ; *str; ++str){
hash = (hash << 4) + *str;
if((g = hash & 0xf0000000)){
hash ^= g >> 24;
hash ^= g;
}
}
return hash;
}
Hash 分離連鎖法の例:
char *search_add(char *s, int flag){
int h;
list l;
int main(int argc, char **argv){
int i;
list p;
char input[STR_MAX];
h = hashpjw(s) % HASHSIZE; /* ハッシュ値を算出 */
for(l = bucket[h]; l; l = l->next){
if(strcmp(l->s, s) == 0) return l->s;
}
if(flag == 0)
return NULL;
l = (list)malloc(sizeof(listelem));
l->s = s;
l->next = bucket[h];
bucket[h] = l;
return l->s;
/* ファイルから読み込んだデータをリストに格納 */
RegistryList(argv[1]);
for(i = 0; i < HASHSIZE ; i++){
for(p = bucket[i]; p != NULL ; p = p->next){
printf("bucket[%d] %s\n", i, p->s);
}
}
}
void RegistryList(char *filename){
FILE *fpIn;
char str[STR_MAX], tmp[STR_MAX];
int i, start;
if((fpIn = fopen(filename, "r")) == NULL){
fprintf(stderr, "Cannot open file [%s].\n", filename);
exit(1);
}
while(fgets(str, STR_MAX, fpIn) != NULL){
sscanf(str, "%s", tmp);
word = (char *)malloc(sizeof(char)*strlen(tmp));
strcpy(word, tmp);
/* リストへの登録を行うので,第二引数を0以外にしておく */
search_add(word, 1);
}
fclose(fpIn);
}
printf("検索ワード:");
scanf("%s", input);
/* 検索のみなので,第二引数のflagを0にする */
if(search_add(input, 0) != NULL){
printf("%s が見つかりました.\n", input);
}
else{
printf("%s は見つかりませんでした.\n", input);
}
}