アルゴリズムのご相談

このQ&Aのポイント
  • DHCPのような動作をするアルゴリズムを考えている最中なのですが、配列で作られたテーブルにユーザ名の一覧があり、ユーザ名をキーワードとして配列のインデックスを抽出する方法を探しています。
  • ユーザ名は何らかのルールに則って作られた文字列ではなく、キーワードを順に比較して探していく方法やソートしなければならない構造は使えません。ユーザの追加・削除がある度にソートし直す必要もありません。
  • 2分探索法などの検索回数を減らす方法はソートされていることが前提条件ですし、ユーザ名から配列のインデックスに使える数値に変換できるような都合のいい関数は存在しないようです。開発言語はCです。
回答を見る
  • ベストアンサー

アルゴリズムのご相談

はじめまして DHCPのような動作をするアルゴリズムを考えている最中なのですが… その案の中でどうしてもクリア出来ない課題があります。 「配列で作られたテーブルにユーザ名の一覧があり、 そのユーザ名をキーワードとして配列のインデックスを抽出したい」 というもので、これだけなら簡単なのですが、以下3つの条件の提示により、無理ではないかと思えて仕方がないです。 ・ユーザ名は何らかのルールに則って作られた文字列ではない。 ・キーワードを頭から順に比較して探していく方法を使ってはいけない。 ・ユーザの追加・削除がある度にソートし直さなければならないような構造にしてはいけない。 2分探索法などの検索回数を減らす方法はソートされてることが前提条件ですし、 ユーザ名から配列のインデックスに使える数値に変換できるような都合のいい関数なんて存在しないように思えるのですが、いかがでしょう? ちなみに開発言語はCです。

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

  • ベストアンサー
  • mikaemi
  • ベストアンサー率50% (33/65)
回答No.1

どうしてそのような制約があるのか理解できませんが、 ユーザ名をキーワードとした配列があるとして、 それとは別にユーザ名から配列のインデクスが引ける ハッシュ表を用意すればできますね(領域の無駄ですけど)。 ユーザ名の削除はあり、既登録のユーザ名のインデクスは 変えたくなく、削除であいた配列要素の再利用をしたい場合には、 フリーインデクスのフリーリストを作っておけばいいですね(これにも また無駄に領域を使わないとなりませんが)。 もちろん、配列がいっぱいになったら、realloc() しないと いけませんが、インデクスで管理しているから更新部分は 配列のみでOKでしょう。 しかし、そもそも、掲出の制約がなぜあるのか。。。 ダメなデータ構造をテクニックでカバーしようとしないで、 データ構造とアルゴリズムを同時に設計したほうがいいのではないですか?^^;

uya_15
質問者

お礼

ハッシュ表という概念を初めて知りました。 ありがとうございます。

その他の回答 (4)

  • mikaemi
  • ベストアンサー率50% (33/65)
回答No.5

こんな感じでいいのかしら?ハッシュ表やフリーリスト操作が面倒だったので、C++の map(バランス木だけど^^)やstackを使っちゃってますが、 ほかのところは、C言語っぽく書いてます(笑) レコードの構造体は、プログラマが勝手にいじれないと仮定しました。 登録レコードは name が NULL でないので、レコードの配列を 直接たどって、登録レコードか空きレコードかがわかるようにしてます。レコードの構造体とフリーリストのためのリンクとを共用体にしてやれば、フリー領域管理のためのメモリは節約できるでしょうね。 ==== #include <stddef.h> #include <stdlib.h> #include <string.h> /* ユーザのレコード: 今は名前のポインタのみ */ struct Record { char *name; }; #include <stack> struct Table { struct Record *prec; /* レコードの配列 */ size_t nrec; /* 登録レコード数 */ size_t sz; /* 配列の大きさ */ std::stack<size_t> flist; /* 空いているレコードのインデクスのリスト */ } table; void iniTable() { const size_t init_size = 3; size_t i; table.prec = (struct Record *)malloc(sizeof(Record) * init_size); table.nrec = 0; table.sz = init_size; for (i = 0; i < table.sz; ++i) { table.prec[i].name = NULL; table.flist.push(i); /* インデクスをフリーリストへ登録 */ } } /* 配列を m だけ大きくする */ void growTable(size_t m) { size_t i; table.prec = (struct Record *)realloc(table.prec, sizeof(Record) * (table.sz + m)); for (i = table.sz; i < table.sz + m; ++i) { table.prec[i].name = NULL; table.flist.push(i); /* インデクスをフリーリストへ登録 */ } table.sz += m; } #include <map> #include <string> typedef std::map<std::string, size_t> map_t; map_t indexMap; /* 配列のインデクス管理マップ */ void finTable() { map_t::iterator i; for (i = indexMap.begin(); i != indexMap.end(); ++i) free(table.prec[i->second].name); free(table.prec); while (!table.flist.empty()) table.flist.pop(); indexMap.clear(); } const size_t errSz = (size_t)-1; /* エラー報告用インデクス値 */ size_t put(const char *user) { const int grow_size = 3; size_t idx; map_t::iterator iter = indexMap.find(user); /* user のレコードがすでにあれば何もしない */ if (iter != indexMap.end()) return iter->second; /*... user は存在しないのでレコードを作る ...*/ /* フリーリストが空なら配列を大きくする */ if (table.flist.empty()) growTable(grow_size); /* フリーリストから空きインデクスをもらう */ idx = table.flist.top(); table.flist.pop(); /* user のレコード設定 */ table.prec[idx].name = strcpy((char *)malloc(strlen(user) + 1), user); indexMap[user] = idx; /* インデクスをマップに登録 */ ++table.nrec; return idx; } size_t get(const char *user) { map_t::iterator iter = indexMap.find(user); if (iter == indexMap.end()) return errSz; else return iter->second; } size_t del(const char *user) { size_t idx = errSz; map_t::iterator iter = indexMap.find(user); if (iter != indexMap.end()) { /* user は存在した */ idx = iter->second; /* user のインデクスを得る */ /* user のレコードを消去 */ indexMap.erase(iter); table.flist.push(idx); free(table.prec[idx].name); table.prec[idx].name = NULL; --table.nrec; } return idx; } #include <stdio.h> int main() { size_t idx; iniTable(); idx = put("saito"); printf("%d %s\n", (int)idx, table.prec[idx].name); idx = put("yamada"); printf("%d %s\n", (int)idx, table.prec[idx].name); idx = put("tanaka"); printf("%d %s\n", (int)idx, table.prec[idx].name); idx = put("suzuki"); printf("%d %s\n", (int)idx, table.prec[idx].name); idx = del("tanaka"); if ((idx = get("takana")) == errSz) printf("OK\n"); idx = put("sato"); printf("%d %s\n", (int)idx, table.prec[idx].name); idx = put("takahashi"); printf("%d %s\n", (int)idx, table.prec[idx].name); finTable(); } ======= % ./a.out 2 saito 1 yamada 0 tanaka 5 suzuki OK 0 sato 4 takahashi

uya_15
質問者

お礼

ここまで丁寧にサンプルを作って下さるなんて…。 がんばって解読してみます

noname#140971
noname#140971
回答No.4

服飾デザイナでプログラマではありませんが・・・。 どんなアルゴリズムを構想するにしろ、データの件数という量は無視できないと思います。 対象が1000レコードと10万レコードじゃ話が違ってくるのじゃないですかね。 実テーブルの制約が絶対条件であれば、仮想テーブルを用意して解決するしかないでしょう。 が、これも量次第では、ユーザ名を仮想テーブル管理アプリーケーションに投げる策も考えることに。 量次第では、構造体変数をバイナリで記録しておいてセーブとロードを繰り返しても良いような気がします。 いずれにしろ、アルゴリズムの問題というよりも、データ量と逃げの関係だと思いますが・・・。

uya_15
質問者

お礼

おっしゃる通りです。 どこかの優先度を妥協しないといけないと思ってました。

noname#39970
noname#39970
回答No.3

2分探索または探索用の独自インデックスを作ったら良いんじゃない? 配列をアドレスとして置き換え表現して インデックスにアドレスがかかれてれば値を挿入するときに探索インデックスに適合するようにしたら良いのでは? ・・・要するに他の人と言ってる事が同じかな

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.2

3条件を言葉通りに受け止めれば、配列の最後から逆順にたどることで実現が可能です。 あるいは、配列の添え字を二分木などに登録しておく方法もありです。

関連するQ&A

  • アルゴリズムについて

    「手作業によるソート」がレポートの課題として出されました。 「手作業のために効率的なアルゴリズムを考えて提案して分析しなさい」 条件は次の通り: 資料がそれぞれ A4 の紙一枚。見やすい位置に10桁の番号が書かれている。その番号の昇順にソートする。番号の分布については一切不明。二つのケースを想定: 1) 一人で資料5000枚をソート; 2) 20人で資料50000枚をソート。二つのケースに使えるアルゴリズム一つ、又は両ケースに使えるアルゴリズム一つでもよい。分析では O() での計算量 (理由つき) と実際に想定される時間。今まで習ったアルゴリズムとの類似点や相違点、手作業の特徴に考量した点などについても論じる。 何をすれば良いのかが分かりません。少しでも作業の道筋を示していただければ幸いです。 よろしくお願いします。

  • アルゴリズムのご相談2

    度々すいません。 また考え方の課題なんですけど、 各配列のDataの中に順不同なバラバラなデータが入っているときに、 その中から最も0に近いIndexでDataが空白になっている箇所を探すには、 どういった検索方法が高速なのでしょうか? この場合、頭から探すしかないのでしょうか? ---------------- | Index | Data ---------------- | 0 | 123 ---------------- | 1 | abc ---------------- | 2 | xyz ---------------- | 3 | ← ---------------- | 4 | 456 ---------------- | 5 | ---------------- | 6 | ABC ---------------- | 7 | XYZ ---------------- | 8 | 789 ---------------- | 9 | ijk ---------------- | … | …

  • 乱数の順位付けのアルゴリズム

    [0,1]の範囲で乱数を1000個、配列に発生させて、小さいもの100個を選び出すアルゴリズムということを考えます(知りたいのは数値と配列番号、むしろ配列番号が大事)。まず想定できるのはソートですが、それにもいろんなものがあり、クイックソート、バブルソートなどが挙げられます。ソートのアルゴリズムは既に開発されつくしたのかもしれませんが、どのようになっているでしょうか。一番高速な(かつ間違いない)な方法が1つあればそれを採用したいのですが。 そして1つ厄介なのですが、そのトップ100個以外の900個はソートする必要がないということです。私が考えているアルゴリズムは、 1.既に100個の選ばれていると仮定(初期はすべて1) 2.新しい1個が来たとき、100個の最低値よりも小さいなら考慮の対象なのだから最低値を交換してその100個でソートする。そうでない場合は何もしない。 3.次の新しい1個を調べて、項目2をする。 4.発生させた1000個でそれを繰り返す。 このアルゴリズムだと100個のソートを何百回かぐらいやることになります。 これをするぐらいだったら1000個のソートを1回やればいいということになるでしょうか(不必要な残りの900個もソートされてしまう)。あるいはその1回の1000個のソートの中でやる必要のない処理を排除する工夫があるかもしれません。 問題が難しくなく、素人っぽくコード化できると思いますが、その分、非効率なところも放置されそうなので高速化できるコードの書き方があったら教えて頂きたいのですが。コードというよりアイディアだけ説明していただいても助かります。実際はフォートランになると思いますが。よろしくお願いします。

  • アルゴリズムの正当性について

    線形探索法のアルゴリズムの擬似コードを書いて、そのアルゴリズムの正当性をループ不変式を用いて証明するという課題があります。 擬似コードは以下のような流れにしようと思いますが、この場合成り立つループ不変はどのようなことになりますか? 配列A[a1..an]に対してv=A[i]ならば添字iを、vがAの中になければNILを出力するアルゴリズムです。 for i ←1 to N if A[i] = v return i return NIL

  • データ構造とアルゴリズムの違いについて教えて頂けないでしょうか。

    データ構造とアルゴリズムの違いについて教えて頂けないでしょうか。 データ構造とアルゴリズムについて学習しています。 (質問事項) ・データ構造とアルゴリズムの違いについて教えて頂けないでしょうか。 詳細に教えて頂けると大変助かります。 (私の現状) たとえば、データ構造は、単純なものでは、配列やコレクション、2分木などの構造で、アルゴリズムは2分木探索の実装方法だと思っています。 データ構造とアルゴリズムについては初心者です。 (現在、就職活動中で、これらを学ぶ必要がありご質問させて頂いています) どうか、皆様、教えて頂いた情報を最大限に活用させていただきますので、(皆様にとってはくだらない質問かもしれませんが…)どうぞよろしくお願いいたします。

  • 文字列探索アルゴリズム(Aho Corasick法)をC言語で組みたい

    はじめまして。 標題のとおり、 文字列探索アルゴリズム(Aho Corasick法)をC言語で組みたいと考えています。 簡単なコード例か、どこか情報元はありませんでしょうか? 文字列「あかさたなはまやらわ」から キーワード「あか」・「たな」を探して 出力として、 「あか」…インデックス0 「たな」…インデックス3 となるようなイメージのプログラミングです。 詳しい方がおりましたら、何卒ご教授お願い致します。

  • c言語 配列 や ソート datファイル読み込みについて

    初投稿でC言語初心者なのでよろしくお願いします。 課題でdatファイルから100万個の数字を読み込んで、ソートのタイムを競うのがでました。 ソートのアルゴリズム等は分かるのですが、100万個の数字を読み込むのがわかりません。 datファイルには、縦にずらっと数字が並べられていてどこを区切り文字としてとりだすのとか。 int配列も100万個も格納できないので3次元配列つかうのかなと思ってみたりしてます。 どうやって格納すればソートで使いやすいかご教授お願いいたします。

  • 二分探索で近似値

    こんばんは。 ソートされたfloatの配列で検索キーに最も近い値のインデックスを取得するにはどのようにしたらよろしいのでしょぅか。 単純に二分探索でやってみましたが正しい値が取得されない場合がありました。

  • アルゴリズムプログラミング

    アルゴリズムにおいて以下のような課題が出たのですかその実行結果を出すためのソースプログラム、または実行結果をどなたか教えてください! (1)バブルソート、選択ソート、挿入ソートプログラムに対して、実行時間(小数点以下2桁まで)、比較回数、代入回数をデータ数50000、100000、150000、200000の4つの場合でそれぞれ測定せよ。ただし対象データはランダム関数SFMTを利用して作成するものとする。 (2) ヒープソート、クイックソートとマージソートプログラムの実行時間(小数点以下2桁まで)、比較回数、代入回数をデータ数50000、100000、150000、200000の4つの場合でそれぞれ測定せよ。ただし対象データはランダム関数SFMTを利用して作成するものとする。 SFMTは以下のサイトからSFMT-srcー1.3.3.zipをダウンロードして解凍する。 http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index-jp.html#SFMT そのうち必要なファイルは sfmt.h sfmt.c sfmt-params.h sfmt-params19937.h を使用する。 どうぞよろしくお願いします。

  • 逐次検索と平均比較回数

    アルゴリズムの解析について勉強しています。アルゴリズムに関して全くの初心者なので、アドバイスをいただけるとありがたいです。 今回、課題で出されたのですが、例として、n個の配列(array)があります。 そのArrayが [ 1, 9, 7, 4, 3, 8, 2]とソートされていない状態だとすると、平均比較回数は、 (n+1)/2と定義されています。 もし、探したいindexが 4 だとすると、4回探さなきゃいけないということだし、もし、探したいindexがn番目というケースもあります。 上記のケースは、探したいindexが1個というケースですが、 もし、n個の配列に要素が2回出現した場合、(nは必ず偶数の配列で、要素が1個だけしかないということはありません。あと、2連続で要素が出現することもありません。それぞれの要素はバラバラに散らばっています) 1. 探したい1indexが1番目の場合、2/n*1 2. 2番目の場合、 { (n-2)/n*2/(n-1) } * 2 3. 3番目の場合、 { (n-2)/n* (n-3)/(n-1)* 2/(n-2) } * 3 ...... となっていくと、教授に言われ、2回目のindexの出現は気にしなくていいと言われました。 となると、探したいindexがn/2までを考慮すればいいとなりますよね? 以上のことをΣなり、!なりを使って表したいたいのですが、できるでしょう? 長くなりましたが、よろしくお願いします。

    • ベストアンサー
    • Java

専門家に質問してみよう