No.5ベストアンサー
- 回答日時:
前回、以下の関数がNが十分、大きいとき、h3の戻り値が分散される旨を記述しましたが、
その理論的な考え方を提示していませんでしたので、以下に記述します。
int h3(char *str){
unsigned int i,v;
int l = strlen(str);
v = 0;
for(i = 0;i <l;i++)
v = v + (str[i] << ((i*7) % 31));
return v % N;
}
ここで、 ((i*7) % 31))は、文字(コードの値)を左へ何ビットシフトするかを示しています。
i=0(1文字目)の時、 0ビット
i=1(2文字目)の時、 7ビット
i=2(3文字目)の時、 14ビット
i=3(4文字目)の時、 21ビット
i=4(5文字目)の時、 28ビット
ここで、4文字以内の場合を考慮すると、完全に全ての文字の組み合わせで、
完全にユニークな値(シノニムの発生なし)であることが、確認できます。
1文字の場合、a~z
2文字の場合、aa~zz
3文字の場合、aaa~zzz
4文字の場合、aaaa~zzzz
の文字列になりますが、
添付図①のようにa=0x61,z=0x7Aであるのでa~zの文字は7ビットの数値で表現されます。
h3の戻り値は
1文字目+2文字目を7ビット左シフト+3文字目を14ビット左シフト+4文字目を21ビット左シフト
の結果をNで割った余りですが、Nが十分大きいので、
1文字目+2文字目を7ビット左シフト+3文字目を14ビット左シフト+4文字目を21ビット左シフト
の結果となります。
この結果は、a~z,aa~zz,aaa~zzz,aaaa~zzzzの文字の組みあわせの場合、完全にユニークな値になります。
(1文字目から4文字目まで生成される値にビット重複がないのでユニークな値になります)
(添付図②を参照ください)
5文字目以降は、シフトの数が変わってくるので、理論上は、ビットの重複が発生し(回数は少ないはずですが)シノニムが発生する可能性があります。
本当に詳しい回答、ありがとうございます。
問題の解答だけでなく、具体的な考え方やソースまで書いていただいて、おかげで理解ができました。
最後の問題のハッシュに関しては、どう考えればいいか手がかりを掴めなかったのですが、衝突を回避するためにビットシフトを用いたということでしょうか。
今回はありがとうございました。
No.4
- 回答日時:
ちなみに、今回の問題のような文字列の管理方法をハッシュ法とハッシュ検索とか呼びます。
http://takatakamanbou.hatenablog.com/entry/2015/ …
h1,h2,h3等が返す値をハッシュ値と呼びますが、この値が重複しない関数が優秀なハッシュ値生成の関数です。(h3が最も優秀)
ハッシュ検索時は、同じハッシュ値が重複した時、それをシノニムと呼びます。
シノニムの数が少ないことが、ハッシュ検索で検索時間を少なくすることになります。
今回のケースでは、bucket[i]の単語の数-1がシノニムの数に該当します。
No.3
- 回答日時:
(3)に関するソースです。
#include <stdio.h>
#include <stdlib.h>
#define N (100)
struct dict_list{
struct dict_list *next; //ア
char *name;
};
struct dict_list *bucket[N];
int strcmp_count;
int h1(char *str){
return *str % N;
}
int h2(char *str){
return (str[0] + str[1]) % N;
}
int search(char *str){
struct dict_list *elem;
int i;
i = h1(str);
elem = bucket[i];
while(elem){
strcmp_count++;
if(!strcmp(elem->name,str))
return -1;
elem = elem->next;
}
return i; //イ
}
int search2(char *str){
struct dict_list *elem;
int i;
i = h2(str);
elem = bucket[i];
while(elem){
strcmp_count++;
if(!strcmp(elem->name,str))
return -1;
elem = elem->next;
}
return i; //イ
}
void insert(char *str){
struct dict_list *new;
int i;
i = search(str);
if (i < 0)
return;
new = malloc(sizeof(struct dict_list));
new->next = bucket[i]; //ウ
new->name = str;
bucket[i] = new; //エ
}
void insert2(char *str){
struct dict_list *new;
int i;
i = search2(str);
if (i < 0)
return;
new = malloc(sizeof(struct dict_list));
new->next = bucket[i]; //ウ
new->name = str;
bucket[i] = new; //エ
}
void display(void){
int i;
struct dict_list *elem;
for (i = 0; i < N; i++){
elem = bucket[i];
if (elem == NULL)
continue;
printf("bucket[%d]=>",i);
while(elem){
printf("[%s]=>",elem->name);
elem = elem->next; //オ
}
printf("[]\n");
}
}
int main()
{
char *data[] = {"hydrogen","belium","lithium","berylium","boron","carbon","nitrogen","oxygen","fluorine","neon"};
char *data1 = "natrium";
int no_elm = sizeof(data)/sizeof(char*);
int i;
printf("no_elm=%d\n",no_elm);
//bucketクリア
for(i=0;i<N;i++) bucket[i] = NULL;
//data追加
for (i = 0 ;i < no_elm;i++){
insert(data[i]);
}
//strcmp_countクリア
strcmp_count = 0;
//data1追加
insert(data1);
//strcmp_count印字
printf("strcmp_count=%d\n",strcmp_count);
//bucket印字
display();
//bucketクリア
for(i=0;i<N;i++) bucket[i] = NULL;
strcmp_count = 0;
//data追加
for (i = 0 ;i < no_elm;i++){
insert2(data[i]);
}
//strcmp_countクリア
strcmp_count = 0;
//data1追加
insert2(data1);
//strcmp_count印字
printf("strcmp_count=%d\n",strcmp_count);
//bucket印字
display();
}
ーーーーーーーーーーーーーーーーーーーーーーーーーーー
以下、実行結果です。
no_elm=10
strcmp_count=2
bucket[2]=>[fluorine]=>[]
bucket[4]=>[hydrogen]=>[]
bucket[8]=>[lithium]=>[]
bucket[10]=>[natrium]=>[neon]=>[nitrogen]=>[]
bucket[11]=>[oxygen]=>[]
bucket[98]=>[boron]=>[berylium]=>[belium]=>[]
bucket[99]=>[carbon]=>[]
strcmp_count=0
bucket[7]=>[natrium]=>[]
bucket[9]=>[boron]=>[]
bucket[10]=>[fluorine]=>[]
bucket[11]=>[neon]=>[]
bucket[13]=>[lithium]=>[]
bucket[15]=>[nitrogen]=>[]
bucket[25]=>[hydrogen]=>[]
bucket[31]=>[oxygen]=>[]
bucket[96]=>[carbon]=>[]
bucket[99]=>[berylium]=>[belium]=>[]
No.2
- 回答日時:
以下、回答です。
(ソースは次回提示)(ア):OK
(イ):OK
(ウ):OK
(エ):OK
(オ):elm=elem->next;ではなく、elem=elem->next;(elmという変数は存在しないので記述ミスと思われる)
(3)の回答
h1=2回
h2=0回
上記は、実際に実行した結果(strcmp_countの印字結果)
上記の理論的根拠
h1は、1文字目を100で割った余りをbucket[i]の添え字として採用している。
従って、natriumと1文字目が同じものは、nitrogen,neonの2つがある。よって2回
h2は、1文字目と2文字目を加算した結果を100で割った余りをbucket[i]の添え字として採用している。
従って、1文字目=n 2文字目=aを加算すると、文字コードは、207となる。
1文字目+2文字目=207となる他の組み合わせは、以下のとおりである。
n m l k j i h g f e d c b
a b c d e f g h i j k l m
1文字目と2文字目が上記の2文字の組み合わせとなるケースは、hydrogen~neonには存在しない。
よって、0回となる。
ちなみに、試みに、hydrogenをmbdrogenに変えて、実行してみれば、1回が表示されることで確認できる。
(4)の回答
Nが十分大きく、辞書に含まれる文字列の数(=登録された単語の数)が、十分大きいという前提なので、
N=10000000 登録した単語の数:Z=100000件とすると、
h1が返す値は、先頭の1文字のみで決まるので26通りとなる。
単語の先頭がaからzまで、均等に分散しているという前提であれば、
1つのbucket[i]に格納される単語の数は、Z/26=100000/26=3846個となる。
(もちろん、bucket[i]に格納される単語の数が0の箇所もあるが、その箇所は使われないので、それは除く)
(ちなみに、aの文字コード=97,zの文字コード=122なので、i=97~99,i=0~22の箇所が使用される)
一方、
h3が返す値は、h3を実際に呼び出してみればわかるが、非常にきれいに分散された値が返される。
つまり、単語の数:ZがNより小さければ、1つのbucket[i]に格納される単語の数は、ほぼ1個と考えて良い。
1つのbucket[i]に格納される単語の数は、1個である。
従って、検索時間の比=1つのbucket[i]に格納される単語の数の比となるので、
h1の時間:h3の時間 = Z/26:1となる。(Z=100000の場合 3846:1)
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- 情報処理技術者・Microsoft認定資格 基本情報技術者試験について 基本情報技術者試験の午後問題についてなのですが、 大門①の情報セキュリテ 1 2022/10/30 00:34
- 情報処理技術者・Microsoft認定資格 応用情報技術者試験の午後問題の過去問の解説 1 2023/01/31 16:32
- 情報処理技術者・Microsoft認定資格 J検【令和3年度後期 情報システム試験 システムデザインスキル】問題1(2)の解き方を教えてください 1 2022/03/22 18:36
- 大学受験 お急ぎの質問です。 現在高3受験生です。次の金曜日に明治大学総合数理学部(現象数理科)の学部別試験が 3 2023/02/13 23:38
- 大学・短大 通信制大学の試験の不正行為について 私は通信制大学に通っており、先日オンラインテストを受けました。あ 2 2023/06/25 16:21
- その他(教育・科学・学問) 高校受験について 1 2022/10/29 11:03
- 行政学 過去問を中心とした勉強について 2 2022/06/01 19:44
- 大学受験 大学院入試があります。不安です。 3 2022/07/31 00:41
- その他(プログラミング・Web制作) python コードについて(初学者です) 3 2023/07/20 14:44
- その他(職業・資格) 至急回答お願いします。 12月初旬に衛生管理者試験を受験します。 そのために、過去問の問題集を購入し 1 2022/10/13 18:33
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語のfor文です。 繰り返しの...
-
strchr() の第2引数はなぜ int ...
-
CStringをwchar_tに変換したい
-
_TCHAR*での引数の読み込み
-
間接操作のレベルとは
-
fgetsなどのときのstdinのバッ...
-
c++ 文字列を入力して、一文字...
-
C言語の入力した文字を反転させ...
-
ftoa の作り方
-
標準ライブラリ関数の自作につ...
-
c言語です。
-
ネットワークにつながっている...
-
str系関数を使わずに二つの文字...
-
構造体のアライメント調整
-
間接参照のレベルが異なっています
-
文字列から空白を取り除きたい...
-
起動時の引数の取得方法が分か...
-
コマンドラインに入力されてい...
-
配列をnビットシフトする
-
RGB→YUV変換のプログラム
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
fgetsなどのときのstdinのバッ...
-
charでの計算?
-
C言語のfor文です。 繰り返しの...
-
charからLPTSTRへの変換方法
-
文字列から空白を取り除きたい...
-
C言語の入力した文字を反転させ...
-
'const char *' 型は 'char *' ...
-
配列をnビットシフトする
-
str系関数を使わずに二つの文字...
-
int main()の・・・
-
atoi( ) の反対をやりたい
-
CStringをwchar_tに変換したい
-
c++ 文字列を入力して、一文字...
-
switch文で文字を比較すること...
-
干支のプログラム
-
3桁区切(コンマ)記号をつけ...
-
絶対パスからのファイル名の切...
-
間接操作のレベルとは
-
間接参照のレベルが異なっています
-
型変換
おすすめ情報
すみませんでした。一枚ずつ補足で上げます。
二枚目
三枚目
4枚目
URL(PDF)
http://dl.multidevice-disc.com/dl/2680-57df2fcf1 …