• ベストアンサー

文字列をハッシュにしなければならないのですが

C言語にさ ファイルの中にある、3バイトunicodeの漢字文字列郡をハッシュテーブルに格納してハッシュを作りたいんですが、取っ掛かりすらつかない状況です。 とりあえず、配列から3バイトの16進数にして、後はその文字列分の16進数を足して、それを割ってキーをつくりテーブルにいれる、としようとしています。 配列から3バイトの16進数にする int joint(char a, char b, char c){ int join = 0; join = a<<8; join = (0x0000FF00 & join) + (0x000000FF & b); join = join<<8; join = (0x00FFFF00 & join) + (0x000000FF & c); return join; } このように16進数にするのですが、最初の取っ掛かりとしてのハッシュについては、どうやったらハッシュテーブルに格納でくるのかいまいちわからないのです。誰かわかりやすく教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
  • chie65535
  • ベストアンサー率43% (8520/19368)
回答No.2

>後はその文字列分の16進数を足して、それを割ってキーをつくりテーブルにいれる、としようとしています。 それは良いとは言えないね。 何故なら「あいう」と「ういあ」「いあう」「あうい」など、文字が入れ替わっただけの文字列が、すべて同じキーになってしまうから。 日本語のように「似たような文字が似たような配列で現われる言語」では、文字が入れ替わっただけの場合にもキーが変わるようにした方が良い。 あと、テーブルに格納する方法は、以下の通り。 ・要素数2048個とかの配列を用意する。 ・その配列の要素は「線形リスト」へのポインタ。初期状態では全部ヌルポインタ。 ・線形リストは構造体で「文字列のポインタ」と「次のポインタ」を持つ。 ・文字列からキー(0~2047)を作る。 ・配列の「キー番目」を見に行く。 ・配列の「キー番目」がヌルポインタなら、リストが1要素だけの線形リストを作り、配列に作ったポインタを格納する。線形リストの「文字列のポインタ」はキーを作った文字列へのポインタ。「次のポインタ」はヌル。 ・配列の「キー番目」がヌルポインタではないなら、線形リストを最後まで辿り、同じ文字列が既にあるか調べる。同じ文字列があるなら線形リストはそのままにする。同じ文字列がないなら線形リストに追加する。

fzgu1984
質問者

補足

ハッシュ関数は、こんな漢字のを使おうと思っています。 // ハッシュ関数 // ... 戻値 : ハッシュ値 // ... 引数 : key(キー), mod(割る数) unsigned int hash(char* key, int mod) {     unsigned int n = 0; // 文字コードを順番に足していく for( n = 0; *key != 0; key++) n = (64 * n + *key)% mod; return n; } しかし、ハッシュテーブルというものに関して未だよくわからないです。

その他の回答 (5)

回答No.6

>> しかし、16進数の文字コードを何で割って、ハッシュテーブルに格納していけばいいのでしょうか? >> やはりFFFFFFなのでしょうか? どう答えたら良いのだろう。。。。 #4様が的確に答えておられるのに。。。 基本的に上手く均等的になるようハッシュ値が求められれば良いのです。 それと、関数の外部から割るための値を貰おうとされているようですが間違いです。 ある一つのハッシュテーブルに格納するためのハッシュ値を求める算出式はユニークでなければなりません。 そうでなければ、ハッシュテーブルに格納したデータは意味の無いものになってしまいます。

回答No.5

ハッシュテーブルのMAXは、分類されるキーにより演算された値の取り得るMAXの値と思えば良いでしょう。 後は、自己参照構造体などで#3様が提供されている図を実現するようなプログラムを書けば良いだけです。

fzgu1984
質問者

補足

私の場合は、漢字と日本語が混じった文字列を16進数として、下記の関数で計算したものが、ハッシュテーブルに入ると考えればいいのですね。 しかし、16進数の文字コードを何で割って、ハッシュテーブルに格納していけばいいのでしょうか? やはりFFFFFFなのでしょうか? // ハッシュ関数 // ... 戻値 : ハッシュ値 // ... 引数 : key(キー), mod(割る数) unsigned int hash(char* key, int mod) {     unsigned int n = 0; // 文字コードを順番に足していく for( n = 0; *key != 0; key++) n = (64 * n + *key)% mod; return n; }

  • chie65535
  • ベストアンサー率43% (8520/19368)
回答No.4

>しかし、ハッシュテーブルというものに関して未だよくわからないです。 要は「辞書から文字列を探すときに、探す回数を減らそう」と言うのが、ハッシュテーブルを作る理由。 ANo.3の図を見て下さい。 辞書に「あいう」「かきく」「いろは」「あう」「させそ」「わおん」の6つが登録されている場合に「させそ」を探そうと思ったとします。 ハッシュテーブルを使わず、最初から全部の文字列を調べて行くと、5番目に「させそ」が出てくるまで、5回チェックしないとなりません。 これが「全部で5つ」ではなく「全部で40万語」だったら?そして「させそ」が39万8314番目にあったとしたら?単語を検索するたびにイライラさせられます。 しかし「『させそ』からハッシュキーの『2』を計算で求め、ハッシュテーブルの2番目を見に行くと『あう』の直後に、すぐに『させそ』が見付かる」のです。 また「かきくけ」を探そうとして、ハッシュキーを計算したら「1」になったとします。ハッシュテーブルの1番目を見るとNULLになっているので「『かきくけ』は未登録で、辞書にない」と言うのがすぐに判ります。 もし「端から全部を探しに行く」だと「未登録の単語を探す場合、全ての単語と比較しないと、未登録なのが判らない」ので、40万語あれば40万回の比較が行われます。 40万語を「文字列により計算で求まる値を使って、均等に1024個に分類する」と、1分類あたり約390個になります。 つまり「全部で40万語あっても、最大でも390~400個くらい調べれば、辞書にあるかどうかが判る」のです。 このように「計算で求まる値で、幾つかに仕分けして分類してから、分類された筈の所だけを調べよう」と言うのが「ハッシュテーブルを作る理由」です。 その為「どのような文字列が来ても、偏らないで均等に分類できるようなキーが必要」なのです。 日本語の場合「良く使われる単語の文字数」や「ひらがななど、よく使う文字」に偏りがあるので、うまく均等になるような計算ルーチンが要ります。

fzgu1984
質問者

補足

ハッシュテーブルのサイズってどのように定義すればいいんですか? 比べようとしてる文字列データが10万行以上あるのですが、配列の個数を10万個取ればいいのですか?

  • chie65535
  • ベストアンサー率43% (8520/19368)
回答No.3

イメージ的には、こんな感じ。 「あいう」「かきく」「いろは」のハッシュキーは0。 「あう」「させそ」のハッシュキーは2。 「わおん」のハッシュキーは5。 0番をテーブルの先頭とした場合、1番、3番、4番は未使用。 図の「矢印」は「ポインタ」を表わしている。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

なんとなく日本語が怪しい気もしますが, そもそも「ハッシュ」がなにか理解できていますか?

fzgu1984
質問者

補足

ハッシュてのは、入力されたデータから乱数を取ってきてインデックスにするものじゃないんですか?

関連するQ&A

  • 文字列の入れ替え

    2つのchar型配列x[],y[]に格納された文字列を入れ替えるプログラムを作成せよ。ただし入替え処理には、関数swap_array(char [],char [])を作成して用いること。という問題なのですが、下記の実行例では、2つの文字列xとyをキーボードから入力し、入れ替えた後に表示している。 <実行例> % ./irekae 文字列x (20字まで)? Mojiretsu 文字列y (20字まで)? Irekae 関数swap_array()を呼び出しました 入替後の x: "Irekae" 入替後の y: "Mojiretsu" % となっているのですが文字列を格納するところまでしかわかりません。 どなたか教えていただけないでしょうか? #include<stdio.h> int main(void) { char x[21]; char y[21]; printf("文字列 (20字まで)? "); scanf("%s", x); printf("文字列 (20字まで)? "); scanf("%s", y);

  • 配列への文字列の格納について

    C言語で文字列を扱う時、配列に1文字ずつ格納していくか、ポインタ変数を使うと思うのですが、 ポインタ変数を使った場合の処理が上手くできずに困っています。 詳細を説明すると、値を取得して、前回取得した値と比較します。 それで、同じだった場合には"chritm1"に格納した矢印を書き込む処理なのですが、 現在はfor文により配列に格納したものを使用していますが、 この部分をポインタ変数を使用した処理(1文字1文字書くのではなく、string型変数の用に書き込みたい) に変更したいのですが、上手くできないという状況です。 何かとっかかりとなるようなことでもかまいませんので、教えてください。 よろしくお願いします。 static int mainA( char *fileName){ int loop, i, j; FILE *fd; /* 前回値格納用 */ char *preval; /* 現在値格納用 */ char *thisval; /* 同値用記号格納用 */ char chritm1[6] = " → "; -- 中略 -- for( i = 0 ; i < 150 ; i++ ) { thisval = redata[i]; /* 値の比較 */ if(preval == thisval) { for( j = 0 ; j < 6 ; j++){ /* 前回値と同じ場合は矢印 */ fprintf( fd, ",%c",chritm1[j]); } }else{ /* 前回値と異なる場合は値 */ fprintf( fd, ",%s", thisval); } preval = thisval; /*完了したら改行 */ fprintf( fd, "\n" ); } -- 以下略 --

  • CSV ⇒ 構造体 ⇒ 文字列配列

    1,あいうえおあいうえお 3,かきくけこ 2,さしすせそさしすせそさしすせそ 3,たちつてと 2,なにぬねの 1,はひふへほはひふへほはひふへほはひふへほ    ・    ・    ・ といったようなCSVファイルがあります。 まずは、それを typedef struct tagSetInf{  int nPattern; // 1or2or3  char szSearch[256]; // 文字列:最大255バイト }SetInf; という構造体に格納していき、そのszSearchの値を nPatternの値に応じて3種類の文字列配列((例)search1,search2,search3)に 格納したいと思っています。 CSVのレコード数は可変です。(たぶん最大数は100くらいだとは思うのですが・・・) 以上の実現方法を教えてください。よろしくお願いいたします。 環境:Win32 API、C/C++。

  • ハッシュ法の質問

    #include <stdlib.h> #include <stdio.h> #include <string.h> #define M 257 int zoo(char *v){ int x; x=0; while(*v) x = 256*x + (*v++); if (x<0) x=(-x); return(x%M);} #define CHARMAX 10000 static int chartop=0; static int charbtm=CHARMAX; static char charheap[CHARMAX]; char *goo(char *s){ char *cp; int i,j,len,result; cp =s; len=0; while(*cp++) len++; len++; if(charbtm - chartop < len){ printf("errrrrrrrrrrrrrrrrrrrr"); exit(1);} result = chartop; j=chartop; chartop += len; cp=s; for (i=0;i<len;i++) charheap[j+i]=(*cp++); return(&charheap[result]);} struct item {char *id; int info;}; static struct item table[M]; void initialize(){ int i; for(i=0; i<M; i++){ table[i].id=goo(" ");}} void enter(char *id1, int info1){ int x; x=zoo(id1); while(strcmp(table[x].id, " ")) x=(x+1)%M; table[x].id =goo(id1); table[x].info=info1;} int search(char *id1){ int x; x=zoo(id1); while(strcmp(table[x].id,id1)) x=(x+1)%M; return(table[x].info);} main(){ int t; initialize(); enter("takahasi",1234); enter("kato",2345); enter("saito",4532); printf("%d\n",search("takahasi")); printf("%d\n",search("kato")); printf("%d\n",search("saito")); } これは学生の学籍番号を登録し、登録した名前から番号を検索するプログラムです。 1.このプログラムでは何人まで登録できますか? 2.その人数を超えた場合何が起こるか。 3.配列charheap、配列tableには何が格納されているか という問題があたのですが上の3つの問題がわかりません。誰か教えてください。1・は10000かなっておもいましたが違うようです。

  • c言語 int型の数字をchar型の配列に

    c言語についてです。 int型の数字をchar型の配列に入れたいです。 関数に対してint型の数字を文字列として渡し、 関数内でchar型の配列に格納したいです。 例として、 a(char a[]){  ~~  ~~ } int main(void){ int x = 5678;  ~~  a(x); } とできるようにしたいです。 しかしこれだと5678という値がそのままchar型の一つの配列に入る?ため正しくないです。 欲しい結果としてはちゃんとa関数内で、 a[1] = '5' a[2] = '6' a[3] = '7' a[4] = '8' となってほしいです。 もともと渡す値が”5678”となっていれば結果は正しく出るのですが、 渡す値がint型と決まっているためどうにかして5678を”5678”とすればいいのではないかと考えています。 つまり5678を単純に文字列に変換すればいいのでしょうか? またプログラム内ではsprintfやatolを使用しないで実現させたいです。 難しいかもしれませんがお願いします。 なんだか説明が下手ですみません。 お願いいたします。

  • 配列について

    こんにちは。 int p[1000]という整数型の配列を作成しました。 例えば、 p[0] = 1; p[1] = 2; p[2] = 3; ・ ・ ・ p[99] = 100; を格納し、配列の長さ?を調べる際、 unsigned int len = 0; while(p[len]!='\0') len++; とすることで調べることが可能でしょうか?p[100]番以降に0が格納されている場合それがナル文字となるのでしょうか? また、char c[1000]と文字列型の配列を作成し、文字列の数値を格納していく場合、一桁であれば要素1個(1Byte?)で格納できますが、二桁以上であれば要素2個以上(2Byte以上←あっていますでしょうか)必要です。 c[0]='1' c[1]='2' c[2]='3' c[3]='4' ・ ・ ・ c[9]='1' c[10]='0' c[11]='1' c[12]='1' ・ ・ ・ ここで問題なのですが、文字列10と文字列11の判別はできないのでしょうか? 110111が連続している場合も値110と値111と判別できないのでしょうか? 値を格納する場合は、整数型のint c[1000]を用意するという事でしょうか? よろしくお願いします。

  • fgetcで全角文字列の読み込み

    ファイルから一文字ずつ全角文字を読み込んで、それを配列を格納する場合、C言語では、どのようなコードになるのですか? 一バイトの場合は簡単ですが、2バイトは分かりません。 fgetcはint型を返すみたいです。 教えて下さい。お願いいたします。

  • int型の変数値をバイト列としてコピー

    あるint型の変数に格納されている情報を、バイト列としてコピーする方法で困っています。 変数の入っている領域をそのままコピーしたいので、memcpyを使うかと思うですが、 コピーされた結果を見ると文字列の並びが逆転しているように見えます。 --サンプルコード抜粋 unsigned int i= 12345; unsigned char *c; c = (char *)malloc(sizeof(int)); printf("i_hex=%x\n",i); memcpy(c,(int *)&i,sizeof(int)); 出力結果 i_hex=3039 cの出力結果 3930000000 単純にmemcpyではダメなのでしょうか? 実行環境は、CentOS(32bit)+gccです。よろしくお願いします。

  • 10進数の2進数文字列変換

    10進数の2進数文字列変換 ◆開発環境 OS:Linux(Ubuntu9.10) コンパイラ:gcc4.4 言語:C ◆質問内容 ポインタ*Aに格納されている10進数を2進数の文字列に変換し、 文字配列Bに格納したい。 バッファとして*fpを準備。 webで調査した結果を以下のソースのようにアレンジしましたが、 fputc関数の処理で、プログラムが強制終了してしまいます。 何か、良い方法ございませんでしょうか。 ◆サンプルソース void Dec_conv_bit(long *A,char *B){ unsigned int bit,n; //ビット比較用 unsigned int *fp = &n; n = (unsigned int)A; for(bit = 1 << sizeof(int)*8-1; bit > 0; bit >>= 1){ fputc((n & bit)?'1':'0',fp); } sprintf(B,"%s",fp); //putchar('\n'); }

  • 文字列を格納する配列を、動的なメモリ確保で処理したい

    文字数が一定でない文字列の一覧を、配列に格納したいと思っています。 "AAA" "BBBBB" "CC" このような文字列を配列に格納するのに普通にやるなら char test[3][24]; for(i=0;i<3;i++){ strcpy(test[i],file[i]); } (便宜上、file[0]には、"AAA",file[1]には"BBBBB"が格納されてるとします) こうやると、test[3][24]の24バイトとか余計に確保した分が無駄になるので、 必要な文字列の長さだけ配列を確保したいと考えています。 で、 char *test[3]; for(i=0;i<3;i++){ //sizeには、各文字列のサイズが格納してあります。ここではその部分省略しますが test[i]= (char*)malloc(size); strcpy(test[i],file[i]); } とやれば (イメージで) test[0]= "AAA" test[1]= "BBBBB" test[2]= "CC" となると思ったのですが、うまく処理されませんでした。 このような処理をさせたい場合根本的に違うのでしょうか。 わかりにくい説明ですが、どなたかご存知の方よろしくお願いします

専門家に質問してみよう