喫茶XRAYトップページ


[7276] StringList ソートの速さ比べ返信 削除
2014/2/28 (金) 16:57:19 taki
StringListに逐次データが追加される場合、追加の度にsortメソッドを実行するより
最初から Sorted := true; にする方が断然速いようです。

※私のPCの環境では…

// 追加後Sort  1,267ms 1,000回 (※10,000回だとなかなか帰ってこない)
  for i := 1 to 1000 do begin
    sl.Append(FormatCurr('000000',Random(1000000)));
    sl.Sort;
  end;

// 初めからTrue 77ms 10,000回
  sl.Sorted := true;
  for i := 1 to 10000 do begin
    sl.Append(FormatCurr('000000',Random(1000000)));
  end;

// ちなみに 追加のみ 5ms 10,000回
  sl.Sorted := false;
  for i := 1 to 10000 do begin
    sl.Append(FormatCurr('000000',Random(1000000)));
  end;

データを最後に追加してから並べ替えるより、データを突っ込むところを探して
追加する方が速いのでしょうね。たぶん。



[7277] Re:StringList ソートの速さ比べ返信 削除
2014/2/28 (金) 17:04:46 Mr.XRAY
▼ takiさん

> StringListに逐次データが追加される場合、追加の度にsortメソッドを実行するより
> 最初から Sorted := true; にする方が断然速いようです。


あっ,これ,提供しているコンポーネントで,変なところがあって,
メールで指摘されたことがあります.去年です.
もかして,そのメールの送信主って taki さんでしたっけ ?

もう,コンポーネントのソースなんか見直したくもないです.
動いているんだから...
なんてこと書いたら,指摘してくださった方に失礼ですね (^^;


[7280] Re2:StringList ソートの速さ比べ返信 削除
2014/2/28 (金) 19:21:54 taki
▼ Mr.XRAYさん

> もかして,そのメールの送信主って taki さんでしたっけ ?

残念ながら違います。
今回、訳あって実験してみました。

どんな訳だって? な〜いしょ。


[7282] Re3:StringList ソートの速さ比べ返信 削除
2014/2/28 (金) 22:47:34 taki
> 今回、訳あって実験してみました。

白状します。

私が使っているStarter版ではデータベースが使えません。(T_T)

そこでStringGridをデータベース代わりに使っています。

JANコード、商品名、仕入れ単価、売り単価などのマスターテーブルや
入庫、出庫の明細テーブルの処理ではどうしてもJANコードでソートしないと
効率が悪くなってしまいます。

StringGridのソートの例はググると色々と出てくるのですが、よく理解
できません。Object云々…???

それで、「JANコード+StringGridのIndex(行番号)」をStringListに
記憶しソートした後にforループで廻せばJANコード順にStringGridの
IndexでStringGridのデータにアクセスできます。

欠点はデータを削除するとStringGridの順番がずれてしまうことです。

やっぱ、データベースを考えた人は偉いですね。



[7284] Re4:StringList ソートの速さ比べ返信 削除
2014/2/28 (金) 23:24:08 Mr.XRAY
▼ takiさん

TStringGird のソートは結構面倒だと思いますね.
TStringList の文字列のソートとなると思いますが,
列が複数あると面倒ですね.多分.

> やっぱ、データベースを考えた人は偉いですね。

本当にすごいと思います.
古い話で申し訳ないのですが(えっ? いつだって昔話でしょうだって?)
昔,データベースの導入のためにテストした時,
特に,その当時の DB/2 (IBM), Oracle に驚きました.
一体どんな仕組みになっているのか不思議です.


[7288] Re5:StringList ソートの速さ比べ返信 削除
2014/3/1 (土) 14:01:12 taki
▼ Mr.XRAYさん
 
> 昔,データベースの導入のためにテストした時,
> 特に,その当時の DB/2 (IBM), Oracle に驚きました.
> 一体どんな仕組みになっているのか不思議です.


はい。今回はStringGridには手をつけずに(Tabelとして)置いて、
StringListをQueryのように使えないかと考えました。

この方法だと、StringGridは何もさわる必要がないので便利です。
一方、Query役のStringListは常にKeyでソートしないといけないので、
StringList.Sort と StringList.Sorted := True ではどちらが
速いのかを調べたわけです。

アイデアは閃いた(昔からあるわ)のですが、実際のプログラムに
組み込むのはこれからです。

たてぐやさんのコードに頼る公算は大です・・・。


[7289] Re6:StringList ソートの速さ比べ返信 削除
2014/3/1 (土) 16:12:20 taki
▼ Mr.XRAYさん
▼ たてぐやさん

こんな感じでプログラムできそうです。

http://mrxray.on.coocan.jp/Halbow/Notes/N023.html
[整数値を記憶させる]も参考にしました。


procedure TForm1.Button7Click(Sender: TObject);
var
  i,ix:integer;
  s:string;
  ItemCode,ItemName:string;
begin

  Randomize;
  listBox1.Clear;           // StringGrid役のデータとみなして下さい
  listBox2.Clear;           // クエリー役 本来はStringList
  listBox2.Sorted := true;  // 常にソートされる設定
  listBox3.Clear;           // 結果の確認表示用

  // データが100回追加される処理をしたことに
  for i := 1 to 100 do begin
    // 商品コード番号をランダムに発生
    ItemCode := FormatCurr('000000',Random(1000000));
   // 確認できるように名前の一部に組み込む
    ItemName := 'Item' + FormatCurr('000',i) + '-A';

    // listBox1の最後にデータが追加され、そのIndexがixに入る
    ix := listBox1.Items.Add(ItemCode + ' ' + ItemName + ' @500円');

    // クエリー役のlistBox2にKeyのコード番号とIndexのixを追加する
    // 追加と同時にソートされる
    listBox2.Items.AddObject(ItemCode,TObject(ix));
  end;

  // コード番号順にソートされたデータを取り出す
  for i := 0 to ListBox2.Items.Count - 1 do begin
    ix := integer(ListBox2.Items.Objects[i]);   // Indexを取り出す

    s := ListBox1.Items.Strings[ix];   // StringGrid役からデータを取り出す

    // ここで商品コード毎の集計処理などをする
    listBox3.Items.Append(s);    // 確認のためにListBox3に書き出す
  end;

end;


実行結果です。

ListBox1は商品コードはランダムです。ListBox2が昇順にソートされています。
ListBox3はListBox2と同じ並びになっているのがわかります。
ハイライトは同じデータです。


[7290] Re7:StringList ソートの速さ比べ返信 削除
2014/3/1 (土) 22:32:34 taki
>  listBox2.Items.AddObject(ItemCode,TObject(ix));

ItemCodeはいいけど、TObject(ix)はlistBox2.Items.SaveToFileで
保存されないみたいですね。

残念!


[7291] Re8:StringList ソートの速さ比べ返信 削除
2014/3/2 (日) 15:28:56 taki
StringList.Sorted := true;
で StringList.Addすると重複データが弾かれるみたいです。

しかし、
StringList.Sorted := true;
StringList.Duplicates := dupAccept;  // 重複を許可する
で StringList.Addすると重複データもソートされていました。

どうやら初期の設定は
StringList.Duplicates := dupIgnore;  // 無視
のようです。
StringList.Duplicates := dupError; とすると、
重複データがAddされた時に例外処理ができるそうです。

先は長い。


[7300] Re9:StringList ソートの速さ比べ返信 削除
2014/3/4 (火) 23:37:26 Mr.XRAY
▼ takiさん

> 先は長い。

がんばれ,がんばれ !! takiさ〜ん.


[7285] Re:StringList ソートの速さ比べ返信 削除
2014/3/1 (土) 08:25:39 たてぐや
▼ takiさん

私はこんなコードを長年使っています。参考になれば。


procedure TFrmMain.GridSort(Grid: TStringGrid; SortCol: Integer);
{===============================================================================
グリッドをソートする。
===============================================================================}
var
  str_St1: TStringList;
  str_St2:  TStringList;
  int_i: Integer;
begin
  str_St1 := TStringList.Create;
  str_St1.Assign(Grid.Cols[SortCol]);

  for int_i := 1 To Grid.FixedRows Do str_St1.Delete(0);
  for int_i := 0 to str_St1.Count - 1 do
        str_St1.Strings[int_i] := str_St1.Strings[int_i] + #1 + Format('%.10d',[int_i]);
  for int_i := Grid.FixedRows to Grid.RowCount - 1 do begin
    str_St2 := TStringList.Create;
    str_St2.Assign(Grid.Rows[int_i]);
    str_St1.Objects[int_i - Grid.FixedRows] := str_St2;
  end;
  str_St1.Sort;

  for int_i := Grid.FixedRows to Grid.RowCount - 1 do begin
    Grid.Rows[int_i].Assign(TStringList(str_St1.Objects[int_i -
    Grid.FixedRows]));
    TStringList(str_St1.Objects[int_i - Grid.FixedRows]).Free;
  end;
  str_St1.Free;
end;


[7286] Re2:StringList ソートの速さ比べ返信 削除
2014/3/1 (土) 11:10:46 taki
▼ たてぐやさん
 
> 私はこんなコードを長年使っています。参考になれば。

たてぐやさん ありがとうございます。
でも、残念ながら私の能力不足のためコードが読み解けません。(T_T)

str_St1.Assign(Grid.Cols[SortCol]);とかは分かります。
ソートのKeyとなるデータをstr_St1にコピーしているのですね。

for int_i := 1 To Grid.FixedRows Do str_St1.Delete(0);は見出しの
部分を取り除いて…

for int_i := 0 to str_St1.Count - 1 do
  str_St1.Strings[int_i] := str_St1.Strings[int_i] +
   #1 + Format('%.10d',[int_i]);
ではKeyとなるデータの後に#1とIndexをくっつけて記憶して…

次に、str_St2.Assign(Grid.Rows[int_i]);では
Gridの行のデータ(レコード単位)をStr_St2にコピーして、

str_St1.Objects[int_i - Grid.FixedRows] := str_St2;で
それをオブジェクトとしてStringListに代入?して・・・

つまり、Str_St1にはKeyの文字列とGridの行のデータを持った状態で
Str_St1.Sortする?

その後、
Grid.Rows[int_i].Assign(TStringList(str_St1.Objects[int_i -
    Grid.FixedRows]));では
str_St1に保存したオブジェクトをTStringListとして取り出して
さらにGridのRowデータとしてコピー(書き換え)する。

で、正しいでしょうか?


[7287] Re3:StringList ソートの速さ比べ返信 削除
2014/3/1 (土) 11:30:35 taki
えっ!
 
> for int_i := 0 to str_St1.Count - 1 do
>   str_St1.Strings[int_i] := str_St1.Strings[int_i] +
>    #1 + Format('%.10d',[int_i]);
> ではKeyとなるデータの後に#1とIndexをくっつけて記憶して…
>


 #1 + Format('%.10d',[int_i]) の部分の役割が…


[7292] Re4:StringList ソートの速さ比べ返信 削除
2014/3/2 (日) 21:41:15 たてぐや
▼ takiさん

 最初に謝ってしまいます。これは私が書いたコードではありません。7年くら
い前に必要生じて、Webで見つけたコードです。ですので、私も読み解いてはい
ません。実際に使ってみたら、思い通りの動作をしてくれるし、汎用性がある
ので、よく使う手続きや関数をまとめたMylib.pasにインプリメントしています。

[7293] Re4:StringList ソートの速さ比べ返信 削除
2014/3/3 (月) 08:27:34 TS
▼ takiさん

良く見ていませんが
>#1 + Format('%.10d',[int_i]) の部分の役割が…
これで同じデータが続いた場合、そのデータ分の元の行の順番は
保たれると思います。

[7294] Re5:StringList ソートの速さ比べ返信 削除
2014/3/3 (月) 16:15:29 taki
▼ TSさん

> これで同じデータが続いた場合、そのデータ分の元の行の順番は
> 保たれると思います。


了解です。

[7295] Re6:StringList ソートの速さ比べ返信 削除
2014/3/3 (月) 16:22:48 TS
▼ takiさん

ちなみに私はDelFusaさんのサイトのNifty の FDELPHI のサンプル蔵アーカイブ
内の下記を使わせてもらっています。

StringGridの並べ替え(2重)
http://delfusa.main.jp/delfusafloor/archive/www.nifty.ne.jp_forum_fdelphi/samples/00092.html


[7297] Re7:StringList ソートの速さ比べ返信 削除
2014/3/3 (月) 18:25:02 Mr.XRAY
こんなのもありますね.
データ量にもよりますが,バブルソートが早いのかな ?

[StringGrid でソートをしたい(1)]
http://hp.vector.co.jp/authors/VA015850/delphi/tips/grid05.html

[StringGrid でソートをしたい(2)]
http://hp.vector.co.jp/authors/VA015850/delphi/tips/grid06.html

[StringGrid でソートをしたい(3)]
http://hp.vector.co.jp/authors/VA015850/delphi/tips/grid07.html

ソートはまだ研究がおこなわれている分野のようです.
数年前に,新しいアルゴリズムの発表が,数学関係の分野であったようです.


[7299] Re8:StringList ソートの速さ比べ返信 削除
2014/3/4 (火) 10:22:10 おかぽん
▼ Mr.XRAYさん

> [StringGrid でソートをしたい(2)]
> http://hp.vector.co.jp/authors/VA015850/delphi/tips/grid06.html


> QuickSort は不安定なソートなので問題がある
との記載がありますけど、

> [StringGrid でソートをしたい(3)]
> http://hp.vector.co.jp/authors/VA015850/delphi/tips/grid07.html


>あ、関数ってポインタで引き渡せるんですよ(笑)
>知らなかった人、そういうものなんです(苦笑)

それなら、「複数列で比較できる関数」をポインタで渡すという解決方法を提示すれば、
QuickSortでもいいんじゃなかと思ったり、思わなかったり。

といいながら、「StringGrid でソートをしたい」の記事を参考にしたという
高速化されたソートの記事をコピペ
[高速マージソート]
http://drang.s4.xrea.com/program/tips/list/sort_mergesort.html


高速化の方法を考えるのが楽しかった時代は、もはや過去の事。
今は、イカに楽して実現するかのほうが主体で、速度はなんて二の次になってます。
PCのスペックも向上したので、それほど気にしなくていいんですよ(たぶん)

[7298] Re:StringList ソートの速さ比べ返信 削除
2014/3/4 (火) 10:08:28 おかぽん
こんにちは。

StringListでソートとなると、どうしても文字列のソートになってしまうから、
あまり使わないので、自分の使う方法。

StringGridで表示する場合、大抵別のクラスで行ごとのデータを持っているから

THoge=class
  Col1: String;
  Col2:Integer;
  Col3:TDateTime;
end;

とか宣言し、TClassListに全データ入れてしまう。

そして、StringGridへの初回表示は、以下のようにObjectsを使って、行と実データを
関連付けしてしまう。

var
  i: Integer;
begin
  //行数確保
  StringGrid.RowCount :=  ClassList.Count + 1;
  //0列にクラス参照設定
  for i := 1 to  StringGrid.RowCount -1 do
  begin
    StringGrid.Objects[0,i] := ClassList.Items[i-1];
    RowUpdate(i);
  end;
end;

各セルへの更新は 関連付けされたデータを参照して行う
procedure TForm1.RowUpdate(aRow: Integer);
var
  hoge: THoge;
begin
  hoge := StringGrid.Objects[0,aRow];
  StringGrid.Cells[0, aRow] := hoge.Col1;
  StringGrid.Cells[1, aRow] := IntToStr(hoge.Col2);
  StringGrid.Cells[2, aRow] := FormatDateTime('yyyy/mm/dd', hoge.Col3);
end;

ソートは、StringList.CustomSort()を使う。

function StringListSortCompareA(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := AnsiCompareString(THoge(List.Objects[Index1]).Col1,
     THoge(List.Objects[Index1]).Col2);
end;
function StringListSortCompareB(List: TStringList; Index1, Index2: Integer): Integer;
begin
  //
end;

var
  stringList: TStringList;
  i: Integer;
begin
  stringList := TStringList.Create;
  stringList.Assign(StringGrid.Cols[0]);
  StringList.CustomSort(StringListSortCompareA);
  StringGrid.Cols[0].Assign(stringList);
   for i := 1 to  StringGrid.RowCount -1 do RowUpdate(i);
  stringList.Free;
end;

TStringListSortCompareを使い、1対1の比較関数を作るので、複数列をキーに並び替えできる。
別途作成した機能で、THogeの内容を更新しても、RowUpdate関数で、内容を反映できる。

個別の比較関数が必要になるので、汎用的ではありませんけど。

INCM/CMT
Cyclamen v3.82