C言語関係掲示板

過去ログ

No.1130 ハッシュを用いてファイルに出現する英単語の頻度を求める。

[戻る] [ホームページ]
No.2165

ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ゆうき(2004/06/26 19:24:28)


学校の課題のなのですが、課題Aとしてファイルに出現する単語の頻度を求めるプログラムを作成せよというのが出て、注意事項として、単語としてはアルファベット列のみを扱えればよい。単語は高々10文字までで打ち切ってよい。例えば、"appropriate"も "apporiation"は"appropriat"という同じ単語とみなしてよい。内部データは配列でも線形リストでもよい。
単語数は高々1000語程度扱えればよい。1000語までしか扱えない場合には、1000語を越えるようファイルに対してはエラー終了するか、警告を出すこと。ファイルの内容に応じて動的に扱える単語数を増やすプログラムならなおよい。とあるのですが、これを自分でウェブや本を参考にしながら、
 
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>

#define MAXCH 10
#define MAXWORD 1000

FILE *openCommandlineFile(int, char **);


typedef struct dt {
     char tango[MAXCH];
    int count;
} Data;

enum{NO,YES};

int WordCnt(Data * tp, int num, char *ptr)
{
  int pos;
  int find;
  char buf[MAXCH+1],*p;
  while(*ptr != '\0'){
    while( *ptr != '\0' && !isalpha(*ptr)){
       ptr++;
    }
    if( *ptr == '\0'){
       break;
    }
   
    
    p=buf;
    for(pos=0;pos<MAXCH && isalpha(*ptr);pos++){
       *p++=*ptr++;/* bufにptrの値を入れていく */
    }
    *p='\0';/*buf文字列の最後に\0を入れる*/
    while(*ptr != '\0' && isalpha(*ptr)){
      ptr++;
    }

    find=NO;
    for(pos=0;pos<num;pos++){/*numは最初0に初期化されているから最初はif(!find)文へいく*/
       if(strcmp(tp[pos].tango,buf)==0){
           tp[pos].count++;
           find=YES;
           break;
       }
    }
    if(!find){/*find=NOのとき*/
       strcpy(tp[num].tango,buf);
       tp[num].count=1;
       num++;
    }
    
  }
 return num;
}
  



int main(int argc, char **argv)
{
    FILE *fp;
    char line[1000];
    Data dataBase[MAXWORD]; 
    int num=0;
    int cnt;
    
    fp = openCommandlineFile(argc, argv);
    if ( fp == NULL ) {
        exit(-1);
    }

    while(fgets(line, sizeof line,fp)!=NULL){
      num=WordCnt(dataBase,num,line);
      if(MAXWORD<num){
         break;
      }
      
    }
                                         
    
    fclose(fp);

   
    printf("頻度  単語\n");
    for ( cnt = 0; cnt < num; cnt++) {
        printf("%4d ", dataBase[cnt].count);
        printf("%s",dataBase[cnt].tango);
      
       

        putchar('\n');                      
       
    }

    return 0;
}

/*
  コマンドライン解析を行い、コマンドラインの第2引数のファイル
  オープンして返す。引数なしの場合は標準入力を返す。
*/
FILE *openCommandlineFile(int argc, char **argv)
{
    FILE *fp;

    if (argc <= 1) {
        return stdin;
    } else if (argc >= 3) {
        fprintf(stderr, "引数が多すぎます。\n");
        return NULL;
    } else { // argc == 2
        fp = fopen(argv[1], "r");
        if (fp == NULL) {
            fprintf(stderr, "ファイルを開けません。\n");
            return NULL;
        }
    }
    return fp;
}



上記のようなプログラムを作成したのですが、課題Bのこれにハッシュを使い高速に動作するようにせよというのがどうしても分かりません。その方法は分離探索法でも線形探索法でもいいとし、ハッシュ関数は
hash("c1c2…cn") = ( c1*(256^(n-1)) + c2*(256^(n-2)) + … + cn ) % p    (c1,c2,…,cnは文字のキャラクターコード pは適度に大きな素数)を使うということなのですが、どなたかご教授いただけないでしょうか?



No.2166

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ぽこ(2004/06/26 20:43:28)


ハッシュ自体が分かっていないんでしょうか?
それともハッシュ表で管理するにあたっての何かが分かっていないのでしょうか?


No.2167

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ゆうき(2004/06/26 20:50:01)


ハッシュ表を作って、どうやってハッシュ表にデータを格納するのかがよくわかりません。・・ハッシュ自体がわかってないです。



No.2168

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ぽこ(2004/06/26 21:43:55)


>ハッシュ表を作って、どうやってハッシュ表にデータを格納するのかがよくわかりません。・・ハッシュ自体がわかってないです。

とすると、まずGoogleでハッシュとアルゴリズム辺りをキーワードにして
調べてみてはいかがでしょうか?
(多分ソースコードとかもあります)


No.2169

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ゆうき(2004/06/26 22:13:44)


>とすると、まずGoogleでハッシュとアルゴリズム辺りをキーワードにして
>調べてみてはいかがでしょうか?
>(多分ソースコードとかもあります)

分かりました。がんばってやってみます。


No.2170

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ゆうき(2004/06/27 00:24:14)


例えば、ハッシュ表を
typedef struct
{
Data **data;
unsigned int size;
}HASHTABLE;
ハッシュ関数は
int hash_func(char *str){
 int n,length,h,weight;
 length=strlen(key);
 for(n=weight=h=0;n<length;++n,++weight)
 {
  if(weight>7)
    weight=0;
  h+=(int)key[n]<<(4*weight);
 }
 return (unsigned int)h%HASHSIZE;
}

とすればよいのでしょうか?
でもハッシュ表へ登録することとどうやってそれWordCnt()やmain()に反映させるのかが分かりません。ハッシュを理解していない質問で不快に思うかもしれませんが、どうか教えてください。


No.2171

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---かずま(2004/06/27 03:58:52)


> でもハッシュ表へ登録することとどうやってそれWordCnt()やmain()に
> 反映させるのかが分かりません。ハッシュを理解していない質問で不快に
> 思うかもしれませんが、どうか教えてください。

では、ハッシュを使ったプログラムのサンプルを書いてみます。
どの程度理解できますか?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HASHSIZE  307

typedef struct Data { char *word; int count; struct Data *next; } Data;

Data *hashtab[HASHSIZE];

unsigned int hash(const char *word)
{
    unsigned int v = 0;
    while (*word) v = v*31 + *word++;
    return v % HASHSIZE;
}

Data *add(const char *word)
{
    Data *p;  unsigned int v = hash(word);

    for (p = hashtab[v]; p && strcmp(p->word, word); p = p->next) ;
    if (p) p->count++;
    else {
        p = malloc(sizeof(Data));
        if (p == NULL) return NULL;
        p->word = strdup(word);
        if (p->word == NULL) { free(p); return NULL; }
        p->count = 1;
        p->next = hashtab[v];
        hashtab[v] = p;
    }
    return p;
}

void word_count(FILE *fp)
{
    char word[256];  int i;  Data *p;

    while (fscanf(fp, "%*[^a-zA-Z]"), fscanf(fp, "%[a-zA-Z]", word) == 1)
        if (add(word) == NULL) puts("out of memory"), exit(1);

    puts("頻度  単語");
    for (i = 0; i < HASHSIZE; i++)
        for (p = hashtab[i]; p; p = p->next)
            printf("%4d  %s\n", p->count, p->word);
}

int main(int argc, char *argv[])
{
    FILE *fp;

    if (argc > 2) puts("too many argument");
    else if (argc <= 1) word_count(stdin);
    else if (fp = fopen(argv[1], "r")) word_count(fp);
    else puts("can't open file");
    return 0;
}



No.2172

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ゆうき(2004/06/27 11:34:18)


Data *add()とword_count()のところが特にわからないです。。文字列の「ハッシュ値」を求めて、ハッシュ表の「ハッシュ値」番目にその文字列の格納されている構造体へのポインタを入れているのでしょうか?


No.2173

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ぽこ(2004/06/27 13:14:20)


>Data *add()とword_count()のところが特にわからないです。。文字列の
>「ハッシュ値」を求めて、ハッシュ表の「ハッシュ値」番目にその
>文字列の格納されている構造体へのポインタを入れているのでしょうか?

Add()は実際にハッシュ値を計算し、ハッシュ表に格納している関数。
word_count()はファイルから1単語ずつ読み出し、それぞれの単語に対し
Add()を呼び出す。その後ハッシュ表に格納されている単語を表示する関数。

ここまでは、分かりますか?
もっと細かいところが分からないのでしょうか?


No.2174

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ゆうき(2004/06/27 13:45:14)


>ここまでは、分かりますか?
>もっと細かいところが分からないのでしょうか?

はい、えーと混乱してきました。すみません、よろしかったらData *add()のところを順を追って説明していただけませんか?


No.2175

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---とおりすがり(2004/06/27 13:52:13)


>すみません、よろしかったらData *add()のところを順を追って説明していただけませんか?

中途半端な質問をしておいて、何を甘えているのやら。
あなたが自分の理解度を順を追って説明して、もっと的を絞った質問を
すべきじゃないのか?


No.2177

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---ゆうき(2004/06/27 14:31:18)


>中途半端な質問をしておいて、何を甘えているのやら。
>あなたが自分の理解度を順を追って説明して、もっと的を絞った質問を
>すべきじゃないのか?

すみません、間違いないです。その通りです。勉強しなおしてきます。


No.2176

Re:ハッシュを用いてファイルに出現する英単語の頻度を求める。
投稿者---あかま(2004/06/27 14:21:10)


>はい、えーと混乱してきました。すみません、よろしかったらData *add()のところを順を追って説明していただけませんか?
「ハッシュ チェイン法」でググると出てくると思います。
同じハッシュキーの文字列をリスト構造で繋げていきます。

その前に、ハッシュ自体が分かっていないとどうにもならないのですが。