プロが教える店舗&オフィスのセキュリティ対策術

今、VisualStudio 2005 C#でデータを読み込んで、グラフにプロットするプログラムを組んでいます。読み込むデータは1行が16項目のテキストファイルです。プロットまでの手順は以下のようになっています。

------------------------------------------------------
(1)予めテキストデータを全て行単位で配列に格納しておく
(2)プロットするデータの条件を決定(任意)
(3)プロット処理開始(ボタンクリックイベント)
(4)1行目から最終行まで探索し、
 条件と一致するデータが見つかれば、行データを分割して対象データを描画
------------------------------------------------------

現状では、ボタンクリックイベントである(3)、(4)の処理に数分間掛かってしまいます。予め配列に格納すれば、(ボタンクリックイベントの際の)ハードディスクへのアクセス時間が短縮されると考え、このようなアルゴリズムを組んだのですが、より効率の良い方法があれば教えて頂けないでしょうか。
よろしくお願いします。

A 回答 (8件)

>(4)1行目から最終行まで探索し、


いわゆるシーケンシャルサーチのようですが
10万行あれば10万回の検索が発生し
条件が100個あるとするなら1000万回の比較が発生しますよね?
2分探索を使えばLog2(10万)=16.6
つまり17 * 100 = 1700回の比較で済みます。

(1)予めテキストデータを全て行単位で配列に格納しておく
(2)プロットするデータの条件を決定(任意)
(3)配列のデータをソート
(4)プロット処置開始
(5)2分探索で目的のデータを検索
(6)一致するものがあれば描画

あと6(描画)はネックにはなっていませんか?
もしボトルネックになっているとするなら毎回描画するのではなく
バックサーフェースを作り(System.Drawing.Graphicsを別に作成)
そこで全てを描画しきってから
Graphics.DrawImageで転送すると速くなるでしょう。

ボトルネックを調べるにはQueryPerformanceCounterを使ってください。
その処理に何クロックかかっているかがわかります。
http://www.divakk.co.jp/aoyagi/csharp_tips_trace …
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。
皆さんのアドバイスを取り入れた上で、その成果をQueryPerformanceCounterで確認したいと思います。

お礼日時:2007/08/02 18:01

補足:


UNIX C、Windows C、Windows VB でBLoad の書き方もそれぞれ。
C# のそれもまた違うでしょう。
要は、アイデアを提示したということ。
「前処理と本処理の2段構えだと、いかようにも本処理を最適化しやすいのでは?」という提案です。
    • good
    • 0

C言語で書いたのは Sony の News が発売された頃。


ですから、そのソースはないです。
ただ、Windows 3.1 の頃に書いたのは残っています。

/*---------------------------------------------------------
* bsave
*---------------------------------------------------------*/
#include <stdio.h>

FILE *file_open(char *, char *,char *);

size_t bsave(fname, vp, recsize, dir)
char *fname;
void *vp;
size_t recsize;
char *dir;
{
  FILE *fp;

  if (fp = file_open(fname, "wb", dir)) {
    recsize = fwrite(vp, recsize, 1, fp);
    fclose(fp);
  } else
    ferr("bsave: セーブエラー", fname, 1);

  return(recsize);
}

bload は、全く、この逆なので割愛します。

/*------------------------------------------------------------
* t_load
*------------------------------------------------------------*/
#include <string.h>
#include <memory.h>

typedef struct{
  char member1[21];
  char member2[21];
} TEST1;

typedef struct{
  char member1[21];
  char member2[21];
} TEST2;

size_t bsave(char *, void *, size_t. char *);
size_t bload(char *, void *, size_t. char *);

main()
{
  TEST1 T1;
  TEST2 T2;

  memset(T1.member1, 0, 21);
  ・・・・
  struct(T1.member1, "T1.member1");
  ・・・・
  bsave("test1.bin", &T1, sizeof(T1), "TMP");
  ・・・・
  lset(T1.member1, "");
  ・・・・
  bload("test.bin", &T1, sizeof(T1), "TMP");
  ・・・・
  putf("&---+----1----+----&0", T1.member1);
  ・・・・

  return(0)
}

[実行結果]

T1.member1
T1.member2

総称的なポインタを使って、BASICのBLOADとBSAVEを再現。(昔は、かかる関数があった)
内容的には、update_bfile関数を書き込みと検索に分け、レコード1とのアクセスに限定。
30MZという当時のボロパソコンでもRAMディスクですと0.11秒でセーブとロードできたとあります。

なお、lset、putf などは自作ライブラリかも知れません。

とにもかくにも10数年前の話しです。
    • good
    • 0

#2です。


ごめんなさい。
ArrayListだと部分一致が難しいを忘れてました。

で、話が戻る形になるのですが、以下のコードでやってみても無理ですかね。

string[] data = { "aaaa", "bbbaaa", "ababab", "asdfaa", ";lkja" };
foreach (string str in Array.FindAll(data, delegate(string s) {return s.IndexOf("aaa") != -1;}))
{
Console.WriteLine(str);
}

多分、該当する列を取得できると思うのですが・・・

だめだったら、格納先をDataTableにしてSelectメソッドで拾ってくるしか考え付きません。

DataTableのSelectメソッド使用のサンプル※MSDN
http://msdn2.microsoft.com/ja-jp/library/det4aw5 …

ごめんなさい。
    • good
    • 0

#2です。


説明ありがとうございます。
状況はわかりました。
(1)は他でも使用するので今回は別として考えてます。
(4)の検索が早くなれば問題ないということですね。
(3)のボタンクリック処理で何をやっているかわからないので何とも言えないのが現状です。


まず、何の配列にデータを格納しているのでしょう。
String配列ですかね。

String配列の内部を検索する場合ですが、配列の数分の文字列を1行1行LOOPして探さなければいけません。
そんなことをしていたら時間が掛かるのは当たり前です。
なので、格納する配列をString配列からArrayListに変更してみてはどうでしょう。
予想ではかなりの時間短縮になると思います。

ArrayList内の検索を行う(中段辺りに記述)
http://dobon.net/vb/dotnet/programing/icomparer. …

どうですかね・・・^^;

この回答への補足

ArrayListを導入してみました。都合上、(文字列の)部分検索を行いたいんですが、行えるような情報がありません…やはり不可能なんでしょうか。

補足日時:2007/08/03 14:05
    • good
    • 0
この回答へのお礼

おっしゃる通り、使っているのはString配列です。やっぱり時間がかかる要因はここだったんですかね。早速ArrayListに移行してみようと思います。

お礼日時:2007/08/02 18:04

BLoad、BSave なんて便利な関数はないですから自作することになります。


これは、いわゆる バイナリロード、バイナリセーブから命名したものです。

CとVBとで作った経験があります。
現在は、Access で同様のことをしています。

' --------------------------------------------------------------------------------
' 構造体変数 MyMenu を Menu.ini に保存
' --------------------------------------------------------------------------------
Private Function BSave(ByVal FileName As String, ByRef MyMenu As MENU) As Boolean
On Error GoTo Err_BSave
  Dim isOK    As Boolean
  Dim intFreeFile As Integer
  
  isOK = True
  intFreeFile = FreeFile
  Open FileName For Random As intFreeFile Len = Len(MyMenu)
  Put #intFreeFile, 1, MyMenu
Exit_BSave:
On Error Resume Next
  Close #intFreeFile
  BSave = isOK
  Exit Function
Err_BSave:
  isOK = False
  Resume Exit_BSave
End Function

A4で10ページ近い Menu.ini を呼び込んで解析し変数に取り込むなんてことをしていたら大変です。
そこで、予め<解析し変数に取り込む>は済ませておく訳です。
この場合、検索が最も簡単・高速になるように構造体変数化します。
C#は存じませんが、Cであれば当然に出来たこと。
まあ、C#も不可能ではないと思います。

' -----------------------------------
' Menu.bin または Menu.ini をロード
' -----------------------------------
If FileExists("Menu.bin") Then
  StopNow = Not BLoad("Menu.bin", MyMenu)
End If

これが、実際に取り込んでいるコード部分です。
構造体変数はこれで出来上がりますので、後は、利用するだけ。
ディスクアクセスもないので超高速になります。

以下は、解析前の Menu.ini です。
この形式を幾ら工夫しても、解析する限りでは限度があると思います。

* +----------------------> アイコンの説明文
* | +--------------> アプリケーション名
* | | +----------> アイコンの場所
* | | | +--> 詳細表示の説明文
* ---------------|-------|---|-------|------------------------
* ----+----1----+----2----+----3--
0-0-0=Icons\Large\売上伝票.ico ' 大きいアイコンの場所
0-0-1=Icons\Small\form.ico ' 小さいアイコンの場所
0-0-2=売上伝票入力 ' アイコンの説明文
0-0-3=F ' F、R、E
0-0-4=売上伝票 ' アプリケーション名
0-0-5=売上伝票フォームを開きます。 ' 詳細表示の説明文
* ------------------------------------------------------------
0-1-0=Icons\Large\仕入伝票.ico
0-1-1=Icons\Small\form.ico
0-1-2=仕入伝票入力
0-1-3=F
0-1-4=仕入伝票
0-1-5=仕入伝票フォームを開きます。
* ============================================================
1=月次処理(&M) ' 最大16桁
* ============================================================
* +----------------------> アイコンの説明文
* | +--------------> アプリケーション名
* | | +----------> アイコンの場所
* | | | +--> 詳細表示の説明文
* ---------------|-------|---|-------|------------------------
* ----+----1----+----2----+----3--
1-0-0=Icons\Large\売上伝票.ico ' 大きいアイコンの場所
1-0-1=Icons\Small\form.ico ' 小さいアイコンの場所
1-0-2=売上伝票入力 ' アイコンの説明文
1-0-3=F ' F、R、E
1-0-4=売上伝票 ' アプリケーション名
1-0-5=売上伝票フォームを開きます。 ' 詳細表示の説明文
* ------------------------------------------------------------
1-1-0=Icons\Large\仕入伝票.ico
1-1-1=Icons\Small\form.ico
1-1-2=仕入伝票入力
1-1-3=F
1-1-4=仕入伝票
1-1-5=仕入伝票フォームを開きます。
END
    • good
    • 0
この回答へのお礼

申し訳ないんですが、VBの知識がほとんど無いのでちょっと読みづらいと言うのが正直なところです。
大変厚かましいお願いなんですが、もし可能ならばCで記述したコードを見せて頂けないでしょうか?

お礼日時:2007/08/01 17:58

考え方が変わってしまうのですが・・・



(1)を一番最初に行う理由は?
もし、無いのであれば、テキストデータを読み込みながら対象文字列がその1行に存在するか確認するロジックの方が良いと思うのですが。
(2)をやって(3)を押してから(1)と(4)を一緒に実行。
現在のままだとボタンをクリックされなくてもテキストデータを配列に格納しているから無駄かなと・・・^^;

1行ずつテキストデータを読み込むサンプル(真ん中辺りから・・・)
http://dobon.net/vb/dotnet/file/readfile.html

文字列内に対象文字列が存在するか検索する方法
http://jeanne.wankuma.com/tips/string/indexof.html


まぁ、VBプログラマーの言うことなので間違っているかもしれませんけど。
    • good
    • 0
この回答へのお礼

ご返答有難う御座います。
(1)を最初に行う理由は、ボタンクリックイベントでのHDへのアクセスを短縮するためです。そして、全てのデータを読み込む理由は別にあります(説明が不足してましたね)。
このプログラムは、データをグラフにプロットして可視化することで、データの特性を見るプログラムで、一旦実行すると何度もプロットを繰り返すことになるんです。ですから、プロットの度に探索を繰り返すよりも、予め全データを読み込んでおいた方が効率的だと考えたんです。ですから(3)(4)が速ければ、(1)にどれだけ時間が掛かっても構わないんです。
説明不足ですみませんでした。

お礼日時:2007/08/01 15:28

>予めテキストデータを全て行単位で配列に格納しておく・・・



これが許されるのならば・・・。

一介の服飾デザイナでプログラマではありません。
が、昨今は、デザイナでもCADも操作しなきゃならないしフロントエンドも開発しなきゃ務まりません。

さて、注文データとCAD内のデータを付き合わせるには実に多くの比較計算が必要。
そのようなデータをオラクルやSQL Serverに登録していたら滅茶苦茶に時間がかかります。
C言語で組んでも1オーダ90秒が限界。
しかし、たった一つの構造体変数で1レコードで記録しておけば BLoad、BSave 関数を使えば一発。
処理過程でのディスクアクセスはゼロに抑えることができます。
この場合、90秒という計算時間は0.01秒以内に短縮することが出来ました。
ある程度のサイズのデータベースも構造体変数にすることも可。

まあ、素人の考えです。
    • good
    • 0
この回答へのお礼

ご返答有難う御座います。
構造体に全てのレコードデータを入れるということですか?
また、BLoad、BSave関数という名前は初めて聞くのでよく分からないのですが、言語は何でしょうか?
こっちも素人なんで…よろしくお願いします。

お礼日時:2007/08/01 15:08

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!