teratail header banner
teratail header banner
質問するログイン新規登録

質問編集履歴

7

もう少し情報を追加しました

2020/05/18 02:14

投稿

aardvark
aardvark

スコア17

title CHANGED
File without changes
body CHANGED
@@ -514,4 +514,9 @@
514
514
 
515
515
 
516
516
  ```
517
- が延々と続いてしまうのです!!!!!
517
+ が延々と続いてしまうのです!!!!!
518
+
519
+ 編集2:
520
+ ごめんなさい、よく考えたらr=hash_search(H, s);→slist_insert_tail(L, r);のときは次のリストの要素を指すポインタもそのままコピーされるのですから、そりゃ無限ループになりますよね、、、
521
+
522
+ もう少し考えます

6

質問文に説明を追加

2020/05/18 02:14

投稿

aardvark
aardvark

スコア17

title CHANGED
File without changes
body CHANGED
@@ -492,7 +492,7 @@
492
492
  return 0;
493
493
  }
494
494
  ```
495
- 例えば、
495
+ 例えば、ハッシュ関数が常に1を出力するよう変更し、
496
496
  ```
497
497
  3
498
498
  coffee コーヒー

5

コードにコメントした

2020/05/18 02:08

投稿

aardvark
aardvark

スコア17

title CHANGED
File without changes
body CHANGED
@@ -419,7 +419,7 @@
419
419
  int h=hfunc(H, key);
420
420
  p = H.T[h]->head;
421
421
  while(p!=NULL){
422
- if(string_compare(p->key, key)==0){
422
+ if(string_compare(p->key, key)==0){ //keyと一致するような要素を同じスロットにあるリストから探索していきます
423
423
  return p;
424
424
  }
425
425
  p = p->next;

4

コードを書き直しましたが、やはりうまくいきません

2020/05/18 02:06

投稿

aardvark
aardvark

スコア17

title CHANGED
File without changes
body CHANGED
@@ -259,4 +259,259 @@
259
259
 
260
260
   自分で調べててみたところ、どうもハッシュ関数を改良したほうがよさそうなので、mの値を数万程度ではなく数百万程度の素数に変えるなどをしてみました。しかし、mにどのような値を入れてもうまくいきません。
261
261
 
262
-  どこを改良するべきか、アドバイスいただけるでしょうか。宜しくお願いします。
262
+  どこを改良するべきか、アドバイスいただけるでしょうか。宜しくお願いします。
263
+
264
+ 編集:
265
+ 衝突が起きる場合を想定して書き直しましたが、今度は同じスロットの要素を出力するときにおかしくなります。
266
+ ```C
267
+ #include <stdio.h>
268
+ #include <string.h>
269
+ #include <stdlib.h>
270
+ #include <math.h>
271
+
272
+ #define NEW(p, n){p = malloc((n)*sizeof(p[0]));}
273
+
274
+ typedef char* String;
275
+ //後々使いやすくするため、ここでStringタイプを定義しちゃいます
276
+
277
+ //英和辞書用のオブジェクトです(英語の見出しと日本語の意味のペア)
278
+ typedef struct slobj_{
279
+ struct slobj_* next;
280
+ String key;
281
+ String jpn;
282
+ }* slobj;
283
+
284
+ //連結リスト
285
+ typedef struct{
286
+ slobj head;
287
+ slobj tail;
288
+ }* slist;
289
+
290
+ typedef struct{
291
+ int n; //ハッシュに格納されている要素数
292
+ int m; //ハッシュ表のサイズ
293
+ slist* T; //リストの配列
294
+ } hash;
295
+
296
+ ハッシュ関数
297
+ int hfunc(hash H, String key){
298
+ int h=0, i=0;
299
+ while (key[i] != 0){
300
+ h = h*3659+key[i]; //ハッシュ値を計算
301
+ i++;
302
+ }
303
+ return abs(h) % H.m; //非負の値にして、表のサイズで割った余り
304
+ //return 1; //ハッシュ関数が全て同じ値を出す場合で実験してみました。
305
+ }
306
+
307
+ //文字列の長さを求める
308
+ int string_len(String str){
309
+ int len=0;
310
+ while(str[len]!=0){
311
+ len++;
312
+ }
313
+ return len;
314
+ }
315
+
316
+ //文字列を標準入力から読む
317
+ String string_input(void){
318
+ int i, len;
319
+ char buf[1000];
320
+ String str;
321
+ scanf("%999s", buf);
322
+
323
+ len = string_len(buf);
324
+
325
+ NEW(str, len+1);
326
+ for(i=0; i<len; i++){
327
+ str[i]=buf[i];
328
+ }
329
+ str[len] = 0;
330
+ return str;
331
+ }
332
+
333
+ //文字列が同じかどうかを判定する関数
334
+ int string_compare(String p, String q){
335
+ int c1, c2;
336
+ int i=0;
337
+ while(1){
338
+ c1=p[i]; c2=q[i];
339
+ if(c1<c2) return -1;
340
+ if(c1>c2) return 1;
341
+ if(c1==0) break;
342
+ i++;
343
+ }
344
+ return 0;
345
+ }
346
+
347
+ //新しい連結リストを作成
348
+ slist slist_new(void){
349
+ slist L;
350
+ NEW(L, 1);
351
+ L -> head = NULL;
352
+ L -> tail = NULL;
353
+ return L;
354
+ }
355
+
356
+ //新しいリスト要素(つまり英和辞典における英語の見出しと対応する日本語のペア)
357
+ slobj slobj_new(String x, String y){
358
+ slobj p;
359
+ NEW(p, 1);
360
+ p -> key = x;
361
+ p -> jpn = y;
362
+ p -> next = NULL;
363
+ return p;
364
+ }
365
+
366
+ //連結リストの先頭に挿入
367
+ void slist_insert_head(slist L, slobj p){
368
+ p -> next = L -> head;
369
+ L -> head = p;
370
+ }
371
+
372
+ //連結リストの末尾に挿入
373
+ void slist_insert_tail(slist L, slobj p){
374
+ if(L -> head == NULL){
375
+ L -> tail = p;
376
+ L -> head = p;
377
+ }
378
+ else if(L -> head == L -> tail){
379
+ L -> tail = p;
380
+ L -> head -> next = p;
381
+ }
382
+ else{
383
+ L -> tail -> next = p;
384
+ L -> tail = p;
385
+ }
386
+ }
387
+
388
+ //連結リストをプリント
389
+ void slist_print(slist L)
390
+ {
391
+ slobj p;
392
+ p = L->head;
393
+ while (p != NULL){
394
+ if(string_compare(p->jpn, "NO")==0){
395
+ printf("%s\n", p->jpn);
396
+ }
397
+ else{
398
+ printf("%s %s\n", p -> key, p -> jpn);
399
+ }
400
+ p = p-> next;
401
+ }
402
+ }
403
+
404
+ //表のサイズがmのハッシュ表を作る
405
+ hash hash_new(int m){
406
+ hash H;
407
+ int i;
408
+ NEW(H.T, m);
409
+ H.m = m;
410
+ for(i=0; i<= m-1; i++){
411
+ H.T[i]=slist_new();
412
+ }
413
+ return H;
414
+ }
415
+
416
+ //キーがkeyである要素を返す
417
+ slobj hash_search(hash H, String key){
418
+ slobj p;
419
+ int h=hfunc(H, key);
420
+ p = H.T[h]->head;
421
+ while(p!=NULL){
422
+ if(string_compare(p->key, key)==0){
423
+ return p;
424
+ }
425
+ p = p->next;
426
+ }
427
+ return p;
428
+ }
429
+
430
+ //ハッシュに入力する関数。同じスロットにハッシュされたすべての要素が連結リストに格納されるようにします。
431
+ void hash_insert(hash H, slobj obj){
432
+ int h=hfunc(H, obj->key);
433
+ slist_insert_head(H.T[h], obj);
434
+ }
435
+
436
+ void slist_free(slist L){
437
+ slobj p, q, r;
438
+ p = L->head;
439
+ q = p->next;
440
+ while (q != NULL){
441
+ r = q;
442
+ q = q-> next;
443
+ free(p);
444
+ p = r;
445
+ }
446
+ free(p);
447
+ free(L);
448
+ }
449
+
450
+ void hash_free(hash H){
451
+ int i;
452
+ for(i = 0; i <= H.m-1; i++){
453
+ if(H.T[i] -> head != NULL){
454
+ slist_free(H.T[i]);
455
+ }
456
+ }
457
+ free(H.T);
458
+ }
459
+
460
+ int main(){
461
+ int i, j, k, n;
462
+ String eng, jpn, s;
463
+ double v;
464
+ slobj p, r;
465
+ hash H;
466
+ slist L=slist_new(); //ここに出力する実数を格納していく
467
+ int m=81239; //数千から数万程度の素数がいいらしい
468
+ //ここから英和辞典を作成します。
469
+ scanf("%d", &n); //英和辞典に記録していく英語日本語ペアの数
470
+ H=hash_new(m);
471
+ H.n = n;
472
+ //英語日本語ペアを順次入れていく
473
+ for(i=1; i<=n; i++){
474
+ eng=string_input();
475
+ jpn=string_input();
476
+ p=slobj_new(eng, jpn);
477
+ hash_insert(H, p);
478
+ }
479
+ //ここからはkeyを入力していきます。各keyに対応する英和辞典の見出しがある場合はそのkeyおよびそれに対応する日本語を、ない場合は-1と出力します
480
+ scanf("%d", &k);//以後の問い合わせの数
481
+ for(j=1; j<=k; j++){
482
+ s=string_input();
483
+ r=hash_search(H, s);
484
+ if(r==NULL){
485
+ r=slobj_new(NULL, "NO");
486
+ }
487
+ slist_insert_tail(L, r);
488
+ }
489
+ slist_print(L);
490
+ //slist_free(L);
491
+ //hash_free(H);
492
+ return 0;
493
+ }
494
+ ```
495
+ 例えば、
496
+ ```
497
+ 3
498
+ coffee コーヒー
499
+ milk 牛乳
500
+ water 水
501
+ 2
502
+ milk
503
+ coffee
504
+ ```
505
+ という入力にした場合、
506
+ ```
507
+ milk 牛乳
508
+ coffee コーヒー
509
+ milk 牛乳
510
+ coffee コーヒー
511
+ milk 牛乳
512
+ coffee コーヒー
513
+
514
+
515
+
516
+ ```
517
+ が延々と続いてしまうのです!!!!!

3

文章を追加

2020/05/18 02:04

投稿

aardvark
aardvark

スコア17

title CHANGED
File without changes
body CHANGED
@@ -255,7 +255,7 @@
255
255
  aucltz
256
256
  (中略)
257
257
  ```
258
- のような長いインプット正しく動作しないようです。
258
+ のような長いインプットだと、間違ったkeyに対応する見出しが何個か出力されてしまい、正しく動作しないようです。
259
259
 
260
260
   自分で調べててみたところ、どうもハッシュ関数を改良したほうがよさそうなので、mの値を数万程度ではなく数百万程度の素数に変えるなどをしてみました。しかし、mにどのような値を入れてもうまくいきません。
261
261
 

2

コードの修正

2020/05/18 00:14

投稿

aardvark
aardvark

スコア17

title CHANGED
File without changes
body CHANGED
@@ -34,10 +34,9 @@
34
34
  int hfunc(hash H, String key){
35
35
  int h=0, i=0;
36
36
  while (key[i] != 0){
37
- h = h*3659+key[i]; //ハッシュ値を計算
37
+ h = h*101+key[i]; //ハッシュ値を計算
38
38
  i++;
39
39
  }
40
- h = h*1000;
41
40
  return abs(h) % H.m; //非負の値にして、表のサイズで割った余り
42
41
  }
43
42
 

1

タイトルの変更

2020/05/18 00:10

投稿

aardvark
aardvark

スコア17

title CHANGED
@@ -1,1 +1,1 @@
1
- ハッシュ関数の選び方についてアドバイスを頂きたいです。
1
+ 英和辞書を作成しているのですが、ハッシュ関数の選び方についてアドバイスを頂きたいです。
body CHANGED
File without changes