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); } }
© Copyright 2024 ExpyDoc