[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『配列のソートをわざわざシート上でやる意義』(中途B)
「そんなの好きにすればいいじゃん」という結論が最初から予想される内容です。 予めご了承ください。
ちょっと先輩と意見衝突しておりまして、 配列のソートを自前でやろうとしている自分に対し、 わざわざクイックソート組んだところで、結局Sortメソッドには勝てない というのが先輩の主張だったのですが、 いくら高速だからと言っても、
1.並べ替え用のシートを追加 2.配列を(必要ならTransposeして)シートに貼り付け 3.並べ替え実行 4.シートの値を(必要ならTransposeして)配列に戻す 5.シートを破棄
どう考えても処理が増えてます。 シート上での並べ替えはそれらを加味しても尚ってほど高速なのか?
と思って、試しに計ってみました。
計測にはtimeBeginPeriod(1)した上でのtimeGetTimeを使用 配列の内容はすべて0以上のランダムな整数です。
| 配列のサイズ(行数x列数) | 1回目 |256x3|4096x3|65536x3|1048576x3| クイックソート | 0| 10| 149| 2807| クイックソート(安定版)| 0| 9| 173| 3210| シート上で並べ替え | 96| 101| 444| 6093| 内)Sort部分のみ | 8| 15| 95| 1789| マージソート | 1| 19| 382| 7419|
2回目 |256x3|4096x3|65536x3|1048576x3| クイックソート | 0| 10| 164| 2840| クイックソート(安定版)| 0| 10| 172| 3247| シート上で並べ替え | 65| 85| 417| 6145| 内)Sort部分のみ | 1| 4| 92| 1798| マージソート | 2| 18| 378| 7652|
予想通り、Sortメソッドそのものは高速ですが、前後処理でロスしてしまいます。 「そもそもシート上で並べ替えできるように設計すべきだ」と言われたらそれまでかもですが、 それでも「元データがシートに無く、配列自体をシートに書き出したい訳じゃない場合も?」 と思ってしまいます。 しかも手順4以外の操作ではぞれぞれイベントが投げられる訳でしょ? Excelの立場からすれば本筋とは無関係な処理をも要求される事になってますよね。 それこそ「わざわざシートに書き込まなくても」って思いません?
「配列 ソート VBA」で検索したら参考記事が山ほど引っ掛かるメジャーな手法だと思ってます。 しかしながら、Sortメソッドを推奨する記事もたくさん引っ掛かります。
先輩はそれなりのExcel歴をお持ちなので、おかしな事を言っておられる訳ではないのでしょう。 しかし今回のは自分的にどうにも釈然としないのです。 先輩に個人的なこだわりがあっただけ? とも考えられますが もはや先輩とこの話題を蒸し返せる雰囲気でもなかったりして^^;
Excelのベテランである方々の感覚として、 配列の並べ替えを自前でやるのは不都合であったり無駄であったり 何かしらマイナス要因や違和感を感じる事なのでしょうか?
ご意見お聞かせ下さい。
< 使用 Excel:Excel2010、使用 OS:Windows7 >
私には難し過ぎるので、別の切り口で・・
>配列のソートを自前でやろうとしている自分に対し
「自前でやろう」とは何ですか?
「個人的に作成・使用」と言う意味なんですか? 「会社として作成・全員で使用」と言う意味なんですか?
その先輩が辞め、新しい後輩が入って来て、新発想のソートメソッドが クイックソートより高速そうだから「自前でやりたい」と言ってきた場合、 中途Bさんは先輩として、なんとアドバイスすることになりますか?
>「元データがシートに無く、配列自体をシートに書き出したい訳じゃない場合も?」 そういうケースが現実に頻発するんですか?
シート方式の処理速度で実際困ったことが生じているんですか?
> 2.配列を(必要ならTransposeして)シートに貼り付け 仮に1048576行あるとするなら、Transposeは使えないから、どっちがいいなんて言う余地はないのでは?
因みに、どっちか1本に絞らないといけないのですか?(少なくとも先輩はシートオンリー?)
(半平太) 2017/07/24(月) 22:07
1) なぜTansposeが必要なのか理解できません。 2) どちらかと言われたら、Sort Methodの方が間違いなく応用性が高い。 一方配列内で複雑な並べ替えをするならそれなりの仕掛けをしないと無理。
例えば、一般に配列内ソートでは大文字・小文字を同等に扱ってくれないし、同じ値でも元の出現順に ならないケースが多い、等を調整する必要があるケースもある。
Sort codeのアルゴリズム自体は大して難解なものではないけど、期待した結果にソートしようとするには それなりの工夫をする必要がありますよ? ( seiya) 2017/07/24(月) 23:16
こんばんわ。
>「元データがシートに無く、配列自体をシートに書き出したい訳じゃない場合も?」
すいません、これだけですけど、こんな使い方をする時ってどんな時なんですか? 私も業務ツールは幾つか作ったけど、データをこんな使い方した時が1度も無いので。
元データがシートに無いのは、まぁテキストデータやAccessのデータとかを直接読込んだり、 CSVやExcelデータでもファイルを開きたくないとか想像できますけど、 最後出力はどうするんですか? 配列のまま保持していたら、何かでエラーが起きたら中身全部消えますよ。
後このパターン以外では、初めか最後には必ずデータはシート上にあると言うことなので、 ソートメソッドの方が有効ですよね。
それと並べ替え条件が複数の時は、書き戻したとしてもソートメソッドの方が早そうな気がしますよね? これに関しては、自分でコード書いて試す気力が沸かず推測だけなので遅かったらすいません。
(sy) 2017/07/25(火) 00:18
>どう考えても処理が増えてます。 20年前から使われているソートメソッドと、 あなたが書いたソートするサブルーチンと、 どちらのほうが品質が担保されていると思いますか?
品質を確認する工数がもったいないと思います。
「俺はちゃんとアルゴリズムを学んで使いこなせる、プログラマーだぜ!」
「エクセルの機能に頼るのは、ただのマクロじゃん?」
っていうへんなプライドがあるのは、中途Bさんのほうでは?
使うほうは、どんなやり方でも気にしませんよ?
処理速度もそうですが、品質が確かなのが一番じゃないですか?
作り手が気にしなければならないのは使ってる道具の良しあしではなく、完成品の品質でしょう。
あぁ、そうか。。。。
エクセルの使い方の、コンプレックスがあるのかも???
(まっつわん) 2017/07/25(火) 07:07
>「元データがシートに無く、配列自体をシートに書き出したい訳じゃない場合も?」
たまにありますかね データではないですが 例えば出荷明細等のファイルを履歴として保存しているフォルダで 二週間以上前なら捨てる そういう場合はシートに吐き出さないですね
ただデータ処理のソートメソッドは速いしとても良い機能だと思ってよく使っています
>中途Bさんは先輩として、なんとアドバイスすることになりますか?
私は半平太さんのこの質問がとても良い質問に思えます
グロスで考える(と云う事を良く話しますが)
クイックソートでも何でもいいのですが
そのアルゴリズムを理解し使いこなす為のスキルアップ時間が はぶかれていませんか?
(まあ コピペしてわけわからず使用してるなら別ですが)
そのあたりの消費時間はどうお考えなのでしょうか
それと 中途Bさんの心内はここで論争をしても変わらない様な気がします
>わざわざシート上で
という先入観があるかぎり 自前(?)でやる事ですっきりするのでは?
この際 配列ソートのスペシャリストになってみては如何でしょうか
その時に はじめてエクセルのソート機能がよいものにみえるかもしれません
(通りすがり) 2017/07/25(火) 07:21
ちなみに「どっちでもいい」とは思いません。
どちらでもできる方が幅が広がって良いのでは?
(K) 2017/07/25(火) 09:10
自分では組めませんが、そのソートコードが再利用できるかどうかじゃないですかねぇ。 レベルの追加とか簡単にできるなら使いやすいと思うのですが、毎度コード書いてデバッグするなら ある程度結果がわかりやすいソートメソッド使ったほうがメンテナンスは簡単だろうとは思います。
よろしければ中途Bさんが考案されたソートコード見せてもらえませんか? (稲葉) 2017/07/25(火) 09:32
半平太さん、seiyaさん、syさん、まっつわんさん、通りすがりさん、稲葉さんレスありがとうございます。 愚痴に付き合っていただいて恐縮です^^;
背景についてちゃんと説明できていない点とか、 今更ながら読み返してみて、稚拙で乱暴な投げかけをしてしまったとちょっぴり反省してます。
みなさんのレスに対する返事は午後にでも正式に返させて頂きますが、 とりあえず今思った事だけ簡単に申し上げますと、
私はソートメソッドそのものを否定している訳ではありません。むしろ良く使います。 但し今回のケースに関しては、クイックソートが妥当だと判断しただけです。 状況に応じて使い分けしてるだけなんです。 それに対してソートメソッドに固執しているのは先輩の方だと感じています。
という感じです。 取り急ぎお礼迄
(中途B) 2017/07/25(火) 09:45
何故クイックソートよりExcelシートのソートを薦めるか? その主目的は、独自ロジックをコーディングしデバッグするには時間がかかるだろう、しかしExcelのソートならコーディングは簡単であり、デバッグは殆ど不要。 性能面は多少劣るかも知れないか、簡単で短時間で作れるということは、後から他の人が機能変更するのも容易。 処理速度より、開発時間効率を優先した意見なのではないか、と思います。
更に、マクロはインタプリンタ動作なので、1行、1文字毎に時間がかかります。それに対して、Excel自身の機能ならば、C言語からコンパイルされたバイナリで動作するので、よほど高性能なロジックでない限り、内部処理の方が高速です。どっちが速いかは実際にコーディングして比較してみないと判りませんが、どっちが速いにせよ、せいぜい数倍程度の差にしかならないかと思います。 それに対して、開発時間の差は圧倒的に内部処理利用の方が短い。 だったら性能より、工数のかからないほうを選ぶべき、と思いますね。
ちなみに、ソート対象が1列しかないならば、.NETのArrayListを利用するという手もありますよ。簡単さと処理速度の両方を満足させる案です。CreateObject("System.Collections.ArrayList") について調べてみてください。
(???) 2017/07/25(火) 09:59
Kさん大変失礼しました。朝は慌てていてレスを見逃しておりました。 ???さんもご意見やArrayListの情報ありがとうございます。
ちょっと一気には書ききれないので途中までになりますが返事していきます。
半平太さんへ >「自前でやろう」とは何ですか? うーん・・・。 その業務がはじき出す「結果」の立ち位置としては「会社として作成・全員で使用」に近いですが、 「方法」については割と好き勝手にアレンジが許容される部分です。 完全に「使う人」イコール「メンテする人」ですから 代々引き継がれていくものというよりは、業務を引継いだ側が自分でメンテしながら改変に対応、 何ならイチから作り直してもOKって感じです。 という意味では「個人的に作成・使用」と言ってしまってよいと考えます。
従って >その先輩が辞め、新しい後輩が入って来て、新発想のソートメソッドが >クイックソートより高速そうだから「自前でやりたい」と言ってきた場合、 >中途Bさんは先輩として、なんとアドバイスすることになりますか? については「そこは思った様に変更していいよ。むしろそれ教えてクダサイ」になります。
> >「元データがシートに無く、配列自体をシートに書き出したい訳じゃない場合も?」 >そういうケースが現実に頻発するんですか? >シート方式の処理速度で実際困ったことが生じているんですか?
エクセルで行う業務全体から言えば今回のは「特殊なケースなのかもな」とは感じております。 シート方式の処理速度で「困っている」とまでは言いませんが、 前後処理も踏まえて「トータルでより高速なもの」を追い求めた結果です。 今回は上述の様な背景の中での事であり 「一般論として、というならいざ知らず、今回このシーンであえてソレなの?」と思う訳です。
>Transposeは使えないから、どっちがいいなんて言う余地はないのでは? はい。それも私が >>わざわざシートに書き込まなくても と思う理由の一つです。 (まぁ実際のところTranspose自体は回避可能ですから、それを理由に先輩に意見はしてませんが)
>どっちか1本に絞らないといけないのですか? これも上述の様な背景の中、結果的に私が自身の業務においてクイックソートを採用したというだけです。 誰かに逆らって強引にクイックソートに変えた。というのではなく、 先輩と意見衝突したのは出来上がった後の話ですね。 もちろん、今後また前後処理を見直すことで更に高速化出来そうならクイックソートは切り捨てです。
>少なくとも先輩はシートオンリー? わかりません。 口ぶりからするとクイックソート自体はちゃんと理解した上でのアドバイスだと受け取っています。
seiyaさんへ >1) なぜTansposeが必要なのか理解できません。 配列を作成する段階から見直す場合であれば、Tansposeしないで済む様に配列を設計しますが、 「配列があります」の部分から課題スタートする場合、 しかもそれをシートに転記するのであれば、配列の第2次元がExcelの列数を超過するケースを想定して 行列を入れ替える必要があります・・・という意味です。 私は極力使わない様に心掛けてますが、可変長配列だったりすると不可避な場合もありますよね。 まあ今回に関しては完全に蛇足です。すみません。
>2) どちらかと言われたら、Sort Methodの方が間違いなく応用性が高い。 > 一方配列内で複雑な並べ替えをするならそれなりの仕掛けをしないと無理。 同感です。 クイックソートにアレンジ加えてたりしてると 自分でも「これ、めっちゃ無駄なことしてる?・・・やめよかな」と疑問を持ったりしました。 ただ本件から脱線する個人的な感覚ですが、 配列をソートするという観点からすると、ソートメソッドの挙動は特殊だと思います。 空白セルの取り扱い方とか、デフォルトでふりがな情報使うとか。 それを加味してもソートメソッドの方が「簡単に何とでも対処できる」ので 一般的には実際勝負にもならないのでしょう。 でも今回、時間短縮を狙った特定の課題の中で >それなりの工夫をする必要 を承知の上で「これはやるべきだ」と判断し、何パターンも検証を重ね、結果的に時間短縮ができたものです。 一般論振りかざして否定するシーンではないと思います。>先輩
syさんへ >こんな使い方をする時ってどんな時なんですか? 今回のは、元データを複数のテキストから読み込み、 複数パターンの集計結果だけが最終的なレポートとしてシート上に出力される という一連の流れの中の「元データ」の配列と「最終的なレポート」の出力前の配列が対象です。
・・・なんか、字面だけみたら「なんでシート上でやらないんだ?」って私でも思ってしまいますね^^; 集計をシートでしないなんてオイ・・・って いえ、 それを元々すべてシート上で行っていたのが、過去何世代かの改良の中で処理時間短縮を狙って 徐々に配列内での処理割合を増やしていった経緯を持ってるヤツなんですね。 その経緯は先輩もご承知のはずなのに 時間短縮の為にシート上の処理を取りやめたものをまたシートに戻すことになる、というか えーと、これちょっと説明するのむずかしいですね。
問題の本質はソート自体よりその前後処理にあるのは私も感じています。 そこを改善すべきじゃないの? というツッコミだったら、私も「そうですよね」と思うんですが、 既にある配列の在り方には言及せず「配列のソートはシート上で」って言われても 「この状況でなんで?」となってしまう訳です。 その拘りは何なのだろうかと。
>後このパターン以外では、初めか最後には必ずデータはシート上にあると言うことなので、 >ソートメソッドの方が有効ですよね。 すみません。ちょっと私が勘違いしてるかもですが、 「今回の件以外の場面ではソートメソッドの方がいいよね」だと解釈して、もちろんYesです。 今回の様な時間短縮に的を絞ったケースでなければ、間違いなくシート上で処理します。
>それと並べ替え条件が複数の時は、書き戻したとしてもソートメソッドの方が早そうな気がしますよね? あ、ヤバいかも。 その部分に関して先輩と私の認識に齟齬があったかも知れません。 先輩は従来通り4回ソートすると思っての発言だった可能性があります。
すみません。とりあえずここまで。続きは夜になります。
(中途B) 2017/07/25(火) 17:42
> 一般論振りかざして否定するシーンではないと思います。>先輩
この意味が分からないけど、 複雑(数値文字混合等)なデータを複数列に対して的確なソート(昇順・降順)が出来て、 尚且つ後継者がそれを継承できるならいいんじゃないの? ( seiya) 2017/07/25(火) 18:32
中途Bさんの会社の方針は分からないですが・・
syさん言う「業務ツール」の位置づけにして、 複数人で検討・製作・利用上の注意のまとめをやり 全員で利用と言うレベルのものにすればいいと思います。
以後、利用者は利用上の注意を守れば(チェック無しで)安心して使える。
それなら会社の資産になります。
会社の資産が増える事に反対する人はいないと思いますが、 その先輩があくまでネガティブなままなら、まぁ実現は無理ですけどね。
中途Bさんが先輩格になったら、後輩たちをこの方針でリードするしかないです。
(半平太) 2017/07/25(火) 19:25
まっつわんさんへ 背景を説明できていなかった私が悪かったと思います。 私が自分でメンテして自分で使うものなんです。 >品質を確認する工数がもったいないと思います。 通常業務に支障が出ない範囲ではありますが、空き時間見つけてチマチマと時間掛けて確認しましたね。 うーん確かにもったいない事をしたのかも知れません。 勉強になったという意味ではムダだとは思いませんが、それは個人的な部分ですからね・・・
>へんなプライドがあるのは、中途Bさんのほうでは? プライドが無いと言ったらウソになりますが、 私はただの経理担当ですからプログラマーの「へんなプライド」は持ってません。・・・よくあるパターンなんですね? むしろ社内を見渡して、かろうじて自信持ってもいいかも? と思えるのはエクセルだけです。(肝心の簿記がまだダメという^^;) 今回に関しては先輩の「ソートメソッドへの拘り」を感じている立場です。
>使うほうは、どんなやり方でも気にしませんよ? >処理速度もそうですが、品質が確かなのが一番じゃないですか? >作り手が気にしなければならないのは使ってる道具の良しあしではなく、完成品の品質でしょう。 今回の課題が処理速度だった。という背景が説明できていなかったので申し訳ありませんでした。 仰ることは分かります。最も気を付けるべき事ですよね。
>あぁ、そうか。。。。 >エクセルの使い方の、コンプレックスがあるのかも??? 痛いところです〜。先輩に対してはめっちゃ持ってます。コンプレックス^^;
通りすがりさんへ > そのアルゴリズムを理解し使いこなす為のスキルアップ時間が はぶかれていませんか? >(まあ コピペしてわけわからず使用してるなら別ですが) > そのあたりの消費時間はどうお考えなのでしょうか 楽をする為の苦労は惜しむべきではない。とは件の先輩の談ですが、私もそう思います。 また今回に関わらず、コピペしてそのまま使うとか、まず考えられないです。 結果的に一語一句違わず同じものになろうが、絶対にイチから自分で書き直します。 今回書いてるクイックソートも多分現時点で7回以上は大規模な書き直しをしてます。 だからこそ先輩と衝突してしまったのでしょうけど・・・ 多分先輩に言わせればまだまだヒヨッコの付け焼き刃コードなんでしょう。 そこは認めるしかないです。
> それと 中途Bさんの心内はここで論争をしても変わらない様な気がします > >わざわざシート上で > という先入観があるかぎり 自前(?)でやる事ですっきりするのでは? そうなんですよね^^; やっぱこのスレの書出しに戻っちゃいます。
> この際 配列ソートのスペシャリストになってみては如何でしょうか > その時に はじめてエクセルのソート機能がよいものにみえるかもしれません さすがにこれはまだまだ道程は長そうです。 勇気付けられるコメントありがとうございます。
Kさんへ > よく、1列目、2列目、3列目と複数の列で昇順の並び替えをすることがありますが、 > そのような場合で比較するとどんな結果になるんでしょうか? いやー、syさんにも言われてハッとしたんですが、 そこの所で先輩と私で考えてる事が違ってたかも知れないんですよね。 私はソート1回での処理を思ってたんですが、先輩は4回ソートするつもりだったかもなんです。 明日ちょっと先輩に確認してみなければと思ってます。 複数回ソートするのなら確かにソートメソッドが処理全体では速くなるかも知れません。
> どちらでもできる方が幅が広がって良いのでは? 私としてはこのスタンスです。先輩は「クイックソートなんてイラネ」派ですね。
稲葉さんへ > 自分では組めませんが、そのソートコードが再利用できるかどうかじゃないですかねぇ。 一応、そこらへんは意識して組んだつもりなんですが・・・ > よろしければ中途Bさんが考案されたソートコード見せてもらえませんか? オェwwww めっちゃコワイwwwww 素人丸だしコードwwwww あぁ・・・でも、せっかく上級者に診て貰えそうなチャンス...
ぁ、ぁあとで貼ります。 お・・・お手柔らかに、ぉオネガイシマスぅ...
???さんへ > 処理速度より、開発時間効率を優先した意見なのではないか、と思います。 > 性能より、工数のかからないほうを選ぶべき、と思いますね。 うーん。そうであれば良いのですが・・・ 今回処理速度を課題とした取り組みだった中でのそれとなると・・・ちょっとモヤモヤしますが、 でも、先輩のお立場的にもソコは重視されて当然ですよね。
やはり皆様に頂いたご意見から察するに 「ソートコード作るヒマがあったら仕事しろ」的なメッセージだったととるべきでしょうね。 (でも言い方ってもんがあるでしょうに) 実際に速度短縮が出来た身としては、正直素直になれない部分もありますが、 まぁ、先輩に再度確認すべき懸案もある事ですし。
先輩の真意は分からないですが、Excelのベテランである方々の感覚として、 クイックソートそのものやスピードがどーこーというのではなく、開発に掛かるコスパと品質の面で。ね? というのが一般的な見識なのだという事は理解できました。 ありがとうございます。
(中途B) 2017/07/25(火) 21:39
> この意味が分からないけど、 すみません。つい。 先輩に対する愚痴です。たいへん失礼しました。
> 複雑(数値文字混合等)なデータを複数列に対して的確なソート(昇順・降順)が出来て、 > 尚且つ後継者がそれを継承できるならいいんじゃないの? あ、いや、そこまでとなるとハードル高いです。 でもそうか。 そうじゃなかったら却下されても文句言えないですね。
> 「業務ツール」の位置づけにして、 > 先輩格になったら、後輩たちをこの方針でリード ありがとうございます。ま〜だまだ先の話ですねー。
ここの回答者は皆さん親切で温かいですね。 殆んど私の愚痴じゃないか。ってスレにちゃんと真面目に応じて下さいます。 改めて。皆さんありがとうございます。
(中途B) 2017/07/25(火) 21:44
Option Explicit Rem 課題検証メモ ================================================================================================= Rem ●再帰処理とループ処理ではどちらが高速かを検証してみたが、大して違わなかったので再帰処理を採用した Rem クイックソートもマージソートも両方検証したけど、やっぱオーバーヘッドなんて無かった。 Rem 問題はクイックソートの弱点にハマった時のスタックオーバーフローをどう回避するかだろうけど、 Rem それはむしろピボットの採取方法の方が工夫すべきポイントであって、非再帰を採用する理由にはならないと判断。 Rem Rem ●ピボットは配列中の9箇所から採取した値のMedianを採用(ココでは挿入ソートでMedianを算出) Rem 配列中央の値のみをピボットする場合の弱点である「山型データ」の場合のスタック低減措置で、 Rem キレイな「山型データ」に対する効果は抜群だった。(おおむね乱順配列と同程度の時間になった) Rem 関数化したからスピード落ちるかと思ったけど、100万件のデータによる実測では気にするほどの差は出なかった。 Rem 本来は[前半][中半][後半]それぞれからMedianを取って更にそのMedianを採用すべきなんだろうけど面倒くなった。 Rem Rem ●再帰レベルが上限を超えたら、そこから下位の部分はマージソートに切り換え Rem イントロソートではヒープソートを使っているが、既存のマージソートを流用した。 Rem 配列サイズから対数取って、なるべくクイックのスピードを活かしたいのでその2倍を上限にした。(←やめた) Rem 配列中央の値のみをピボットする場合であれば、 Rem 最悪ケースの場合の再帰レベルは桁違いにデカいので2倍と言わず3倍4倍でも良いが、 Rem 今回は9箇所からピボットしてるから、そうそう最悪ケースに陥ることは無いだろう。 Rem せいぜい1倍か1倍+1がちょうどギリ上限くらいなんじゃなかろうか。 Rem ちなみに最初は定数で固定値にしようと思ってて、140万件の山型データに対して上限を32して実測した結果、 Rem 深度は27〜31くらいでギリギリオーバーしなかった。 Rem 試しに上限を16にしたら上限オーバーでマージソートに切り換わったが、 Rem (このマージソートは、同じ大きさの配列に対してクイックソートの2.7倍〜3倍の時間が掛かる) Rem この場合でも処理時間は32の場合とほぼ同じだったので、定数にしてしまっても割と妥当な閾値になると思うし、 Rem 対数のままでも、対数の2倍でも結果に大差は無いんじゃないかと思う。 Rem 1600万件の1次元配列で対数×1と対数×2で比較したら、対数×1でマージソートに切り換わっても Rem 掛かる時間はほぼ同じだった。データの並びに寄っては対数×1の方が微妙に速かったり(と言っても誤差範囲)。 Rem Log2付近になると要素数もかなり小さい範囲での処理になるから、マージソートでも充分高速なんだろうな。 Rem やっぱ対数×1でいこう。 Rem Rem ●ソートしない方の次元のサイズが4以上の場合は外部スワップに切り換え Rem 1.キー列(行)と元添字をセットにした仮配列をつくり Rem 2.仮配列をクイックソートして Rem 3.ソート後の元添字に従って元配列をスワップ Rem という3段階処理。 Rem ソートしない方の次元のサイズが4〜5あたりで、内部スワップ版の速度に追い付いたので閾を4以上にした Rem Rem ●無理やり安定ソートにするオプション付けた Rem この場合はソートしない方の次元のサイズが4未満でも強制的に外部スワップに切り換える。 Rem 1.キー列(行)と元添字をセットにした仮配列をつくり Rem 2.仮配列をクイックソートして Rem 3.仮配列の同じ値が連続している区間の元添字を更に昇順でクイックソートし Rem 4.ソート後の元添字に従って元配列をスワップ Rem という4段階処理。 Rem 結果、配列全体に対して2回ソートする事になるけど、実測したら時間は1割〜2割増し程度だった。 Rem 安定ソートといえばマージソートと思ってたけど、クイックソート捨てるべきではない? Rem Rem ●配列が既にソート済みかどうかを事前チェックしたら遅くなるかどうかの検証 Rem 2097152行で実測した時の処理時間で比較した。計測にはtimeBeginPeriod(1)した上でのtimeGetTimeを使用 Rem 元データの並び Rem 同値・・・すべて整数の1 Rem 昇順・・・整数1〜2097152(重複なし) Rem 降順・・・整数2097151〜0(重複なし) Rem 乱順・・・0以上2097151以下のランダムな整数(重複あり) Rem 山型・・・前半が昇順と同じで後半が降順と同じ Rem 波型・・・1〜10まで昇順で11〜20まで降順(9〜0)、21番目以降その繰り返し Rem 百色・・・0〜99の整数をそれぞれ100コずつ配置、10000番目以降その繰り返し Rem 二色・・・0と1を交互に配置 Rem ▼ソート済みの事前チェックなしの場合の処理時間(秒) Rem |____配列サイズ____|__ソート手段___|_同値_|_昇順_|_降順_|_乱順_|_山型_|_波型_|_百色_|_二色_| Rem |1次元 2097152 | Quick | 3.8 | 2.1 | 2.2 | 4.2 | 4.0 | 3.4 | 3.4 | 3.6 | Rem |__________________|_Merge_________|__6.4_|__6.4_|__6.5_|__8.7_|__6.6_|__8.4_|__7.4_|__7.5_| Rem |2次元 2097152 x 1 | Quick | 4.5 | 2.4 | 2.5 | 4.9 | 4.8 | 3.9 | 3.9 | 4.1 | Rem | | Quick(Stable) | 12.6 | 2.8 | 3.0 | 6.7 | 7.0 | 11.4 | 10.2 | 11.8 | Rem |__________________|_Merge_________|__7.9_|__7.6_|__7.8_|_10.2_|__7.7_|__9.8_|__8.5_|__8.8_| Rem |2次元 2097152 x 2 | Quick | 4.3 | 2.3 | 2.5 | 5.2 | 5.2 | 4.1 | 4.0 | 3.9 | Rem | | Quick(Stable) | 12.1 | 2.8 | 2.8 | 6.7 | 6.7 | 11.1 | 9.9 | 11.4 | Rem |__________________|_Merge_________|_10.9_|_10.3_|_10.3_|_13.5_|_10.6_|_12.7_|_12.1_|_12.4_| Rem |2次元 2097152 x 4 | Quick | 7.7 | 3.1 | 3.2 | 6.8 | 6.0 | 7.2 | 6.5 | 7.1 | Rem | | Quick(Stable) | 13.0 | 3.2 | 3.4 | 7.8 | 7.3 | 12.4 | 10.7 | 12.3 | Rem |__________________|_Merge_________|_10.5_|_10.4_|_10.9_|_14.0_|_10.6_|_13.1_|_11.6_|_11.9_| Rem |2次元 2097152 x 8 | Quick | 8.7 | 3.7 | 3.8 | 8.2 | 6.6 | 8.3 | 7.1 | 8.1 | Rem | | Quick(Stable) | 13.8 | 3.7 | 3.9 | 9.1 | 8.0 | 13.0 | 11.2 | 12.8 | Rem |__________________|_Merge_________|_11.2_|_11.3_|_11.9_|_16.1_|_11.8_|_15.0_|_12.6_|_12.6_| Rem ▼ソート済みの事前チェックありの場合の処理時間(秒) Rem |____配列サイズ____|__ソート手段___|_同値_|_昇順_|_降順_|_乱順_|_山型_|_波型_|_百色_|_二色_| Rem |1次元 2097152 | Quick | 0.1 | 0.1 | 0.2 | 3.7 | 4.0 | 0.7 | 1.0 | 0.3 | Rem |__________________|_Merge_________|__0.1_|__0.1_|__6.4_|__8.0_|__3.5_|__7.4_|__3.4_|__7.4_| Rem |2次元 2097152 x 1 | Quick | 0.1 | 0.1 | 0.3 | 4.4 | 4.6 | 0.9 | 1.2 | 0.4 | Rem | | Quick(Stable) | 0.1 | 0.1 | 0.7 | 6.0 | 6.4 | 6.0 | 4.9 | 3.5 | Rem |__________________|_Merge_________|__0.1_|__0.1_|__7.7_|__9.7_|__4.3_|__8.8_|__4.0_|__8.8_| Rem |2次元 2097152 x 2 | Quick | 0.1 | 0.1 | 0.3 | 5.2 | 5.5 | 1.0 | 1.6 | 0.5 | Rem | | Quick(Stable) | 0.1 | 0.1 | 0.9 | 6.5 | 6.9 | 6.4 | 5.1 | 3.7 | Rem |__________________|_Merge_________|__0.1_|__0.1_|_11.2_|_12.9_|__5.9_|_12.2_|__5.7_|_12.3_| Rem |2次元 2097152 x 4 | Quick | 0.1 | 0.1 | 1.3 | 6.4 | 6.0 | 2.4 | 2.4 | 1.4 | Rem | | Quick(Stable) | 0.1 | 0.1 | 1.1 | 7.1 | 6.9 | 6.8 | 5.4 | 4.2 | Rem |__________________|_Merge_________|__0.1_|__0.1_|_10.8_|_13.4_|__6.1_|_12.0_|__6.1_|_12.0_| Rem |2次元 2097152 x 8 | Quick | 0.1 | 0.1 | 1.6 | 8.0 | 6.5 | 3.3 | 3.0 | 2.1 | Rem | | Quick(Stable) | 0.1 | 0.1 | 1.8 | 8.7 | 7.5 | 7.7 | 6.1 | 4.7 | Rem |__________________|_Merge_________|__0.1_|__0.1_|_11.1_|_14.7_|__6.6_|_13.0_|__6.7_|_12.6_| Rem ▼処理時間の削減率(△は処理時間増) Rem |____配列サイズ____|__ソート手段___|_同値_|_昇順_|_降順_|_乱順_|_山型_|_波型_|_百色_|_二色_| Rem |1次元 2097152 | Quick | 98% | 97% | 92% | 10% | 2% | 79% | 71% | 91% | Rem |__________________|_Merge_________|__99%_|__99%_|___2%_|___8%_|__48%_|__11%_|__54%_|___1%_| Rem |2次元 2097152 x 1 | Quick | 98% | 97% | 90% | 10% | 3% | 78% | 68% | 90% | Rem | | Quick(Stable) | 99% | 97% | 76% | 11% | 9% | 47% | 52% | 70% | Rem |__________________|_Merge_________|__99%_|__99%_|___1%_|___5%_|__45%_|__10%_|__53%_|_△1%_| Rem |2次元 2097152 x 2 | Quick | 98% | 97% | 87% | 1% | △5% | 75% | 60% | 86% | Rem | | Quick(Stable) | 99% | 97% | 69% | 3% | △2% | 42% | 48% | 68% | Rem |__________________|_Merge_________|__99%_|__99%_|_△9%_|___5%_|__44%_|___4%_|__53%_|___1%_| Rem |2次元 2097152 x 4 | Quick | 99% | 97% | 61% | 5% | △1% | 67% | 63% | 81% | Rem | | Quick(Stable) | 99% | 98% | 66% | 9% | 6% | 45% | 49% | 66% | Rem |__________________|_Merge_________|__99%_|__99%_|___1%_|___4%_|__43%_|___8%_|__48%_|_△1%_| Rem |2次元 2097152 x 8 | Quick | 99% | 98% | 58% | 3% | 3% | 60% | 58% | 74% | Rem | | Quick(Stable) | 99% | 98% | 55% | 4% | 5% | 41% | 46% | 64% | Rem |__________________|_Merge_________|__99%_|__99%_|___7%_|___9%_|__44%_|__14%_|__47%_|___0%_| Rem 同値と昇順は結局1回もスワップが発生ないのでやはり効果大 Rem 降順はクイックソートでは再帰回数が1回で済むので効果大 Rem 肝心の乱順の削減率は小さいが逆効果にはならず、事前チェックが「余計な事」ではない証明となった Rem 山型は再帰レベルの上限を超えてマージソートに切り替わっている部分があるので一概には判断できないが、 Rem それでも逆効果の程度はほんの数%であり、マージソート単体でも効果が出ているのでやはりあった方が良い Rem 波型等重複の多いデータはクイックソートもマージソートもそれなりに効果が出ている Rem 総合的な判断としては、事前チェックはやった方がよさそうだ Rem ==========================================================================================================
Rem クイックソート1次元配列用 =============================================================================== Public Sub QuickSort(Target() As Variant, indexFrom As Long, indexTo As Long, Optional Desc As Boolean) Rem オプション ********************************** Rem Decs --------- 降順に並べる Rem ********************************************* Rem 対象区間が既にソート済みだったら何もしないで抜ける If IsSorted(Target, indexFrom, indexTo, Desc) Then Exit Sub Static LvA As Long, LvB As Long, LvLMT As Long Dim CurA As Long, CurB As Long Dim Pivot As Variant, SWP As Variant CurA = indexFrom CurB = indexTo Pivot = GetPivot(Target, indexFrom, indexTo) Do If Desc Then '降順指定の場合 Do Until Target(CurA) <= Pivot '先頭から標本値以下の値を探索 CurA = CurA + 1 Loop Do Until Target(CurB) >= Pivot '終端から標本値以上の値を探索 CurB = CurB - 1 Loop Else '昇順指定の場合 Do Until Target(CurA) >= Pivot '先頭から標本値以上の値を探索 CurA = CurA + 1 Loop Do Until Target(CurB) <= Pivot '終端から標本値以下の値を探索 CurB = CurB - 1 Loop End If If CurA >= CurB Then Exit Do 'A座標とB座標が交差したら終了 If Target(CurA) <> Target(CurB) Then '←この判定が処理時間に与える影響は1%程度でデータの並びによる SWP = Target(CurA) 'それまではA座標とB座標の中身を交換し、 Target(CurA) = Target(CurB) Target(CurB) = SWP End If CurA = CurA + 1 '次のA座標とB座標から再度探索開始 CurB = CurB - 1 Loop If LvA + LvB = 0 Then LvLMT = -Int(-(Log(indexTo - indexFrom + 1) / Log(2))) '再帰レベルの上限算出 If indexFrom < CurA - 1 Then LvA = LvA + 1 If LvA > LvLMT Then Call MergeSort(Target, indexFrom, CurA - 1, Desc) '再帰レベルの上限を超えたらマージソートに切替 Else Call QuickSort(Target, indexFrom, CurA - 1, Desc) 'Aパートに対して同じ処理 End If LvA = LvA - 1 End If If indexTo > CurB + 1 Then LvB = LvB + 1 If LvB > LvLMT Then Call MergeSort(Target, CurB + 1, indexTo, Desc) '再帰レベルの上限を超えたらマージソートに切替 Else Call QuickSort(Target, CurB + 1, indexTo, Desc) 'Bパートに対して同じ処理 End If LvB = LvB - 1 End If If LvA + LvB = 0 Then LvLMT = 0 End Sub Private Function GetPivot(Target() As Variant, indexFrom As Long, indexTo As Long) As Variant Const PIVOT_SIZE As Long = 9 '枢軸候補の採取数 If indexTo - indexFrom + 1 < PIVOT_SIZE * 3 Then '定数×3未満の配列は配列中央の値を採用 GetPivot = Target((indexFrom + indexTo) \ 2) Else 'それより大きい配列は定数分の値を採取し、そのMedianを採用(MedianOfMedianではない簡略版で) Dim Ary(0 To PIVOT_SIZE - 1), i As Long For i = 0 To PIVOT_SIZE - 1 Ary(i) = Target(Int((indexTo - indexFrom) / (PIVOT_SIZE - 1) * i) + indexFrom) Next Call InsertionSort(Ary, 0, PIVOT_SIZE - 1) GetPivot = Ary((PIVOT_SIZE - 1) \ 2) End If End Function Rem クイックソート2次元配列用 =============================================================================== Public Sub QuickSort2(Target() As Variant, OrderBy As Long, indexFrom As Long, indexTo As Long _ , Optional Desc As Boolean, Optional FirstDim As Boolean, Optional Stable As Boolean) Rem オプション ********************************** Rem Decs --------- 降順に並べる Rem FirstDim ----- 第1次元をソートする Rem Stable ------- 安定ソート化 Rem ********************************************* Rem 対象区間が既にソート済みだったら何もしないで抜ける If IsSorted2(Target, OrderBy, indexFrom, indexTo, Desc, FirstDim) Then Exit Sub Static LvA As Long, LvB As Long, LvLMT As Long Dim CurA As Long, CurB As Long Dim Pivot As Variant, SWP As Variant, IdxL As Long, IdxU As Long, j As Long If FirstDim Then IdxL = LBound(Target, 2) IdxU = UBound(Target, 2) Else IdxL = LBound(Target, 1) IdxU = UBound(Target, 1) End If Rem ソートしない方の次元のサイズが大きい(4以上)場合は外部スワップに切り換え Rem また安定ソート指定の場合は次元のサイズに関わらず外部スワップに切り換え If Stable Or (IdxU - IdxL + 1 >= 4) Then Call QuickSort2Ext(Target, OrderBy, indexFrom, indexTo, Desc, FirstDim, Stable) Exit Sub End If Rem 以下内部スワップ版の処理 _________________________________________________________________________________ If FirstDim Then CurA = indexFrom CurB = indexTo Pivot = GetPivot2(Target, OrderBy, indexFrom, indexTo, True) Do If Desc Then Do Until Target(CurA, OrderBy) <= Pivot CurA = CurA + 1 Loop Do Until Target(CurB, OrderBy) >= Pivot CurB = CurB - 1 Loop Else Do Until Target(CurA, OrderBy) >= Pivot CurA = CurA + 1 Loop Do Until Target(CurB, OrderBy) <= Pivot CurB = CurB - 1 Loop End If If CurA >= CurB Then Exit Do If Target(CurA, OrderBy) <> Target(CurB, OrderBy) Then For j = IdxL To IdxU SWP = Target(CurA, j) Target(CurA, j) = Target(CurB, j) Target(CurB, j) = SWP Next End If CurA = CurA + 1 CurB = CurB - 1 Loop Else CurA = indexFrom CurB = indexTo Pivot = GetPivot2(Target, OrderBy, indexFrom, indexTo, False) Do If Desc Then Do Until Target(OrderBy, CurA) <= Pivot CurA = CurA + 1 Loop Do Until Target(OrderBy, CurB) >= Pivot CurB = CurB - 1 Loop Else Do Until Target(OrderBy, CurA) >= Pivot CurA = CurA + 1 Loop Do Until Target(OrderBy, CurB) <= Pivot CurB = CurB - 1 Loop End If If CurA >= CurB Then Exit Do If Target(OrderBy, CurA) <> Target(OrderBy, CurB) Then For j = IdxL To IdxU SWP = Target(j, CurA) Target(j, CurA) = Target(j, CurB) Target(j, CurB) = SWP Next End If CurA = CurA + 1 CurB = CurB - 1 Loop End If If LvA + LvB = 0 Then LvLMT = -Int(-(Log(indexTo - indexFrom + 1) / Log(2))) If indexFrom < CurA - 1 Then LvA = LvA + 1 If LvA > LvLMT Then Call MergeSort2(Target, OrderBy, indexFrom, CurA - 1, Desc, FirstDim) Else Call QuickSort2(Target, OrderBy, indexFrom, CurA - 1, Desc, FirstDim) End If LvA = LvA - 1 End If If indexTo > CurB + 1 Then LvB = LvB + 1 If LvB > LvLMT Then Call MergeSort2(Target, OrderBy, CurB + 1, indexTo, Desc, FirstDim) Else Call QuickSort2(Target, OrderBy, CurB + 1, indexTo, Desc, FirstDim) End If LvB = LvB - 1 End If If LvA + LvB = 0 Then LvLMT = 0 End Sub Rem クイックソート2次元配列用(外部スワップ) ================================================================= Private Sub QuickSort2Ext(Target() As Variant, OrderBy As Long, indexFrom As Long, indexTo As Long _ , Desc As Boolean, FirstDim As Boolean, Stable As Boolean) Dim SWP() As Variant, IdxL As Long, IdxU As Long Dim Key() As Variant, r As Long, c As Long ReDim Key(0 To 1, indexFrom To indexTo) If FirstDim Then For r = indexFrom To indexTo Key(0, r) = r Key(1, r) = Target(r, OrderBy) Next Call QuickSort2Indexer(Key, indexFrom, indexTo, Desc) If Stable Then Call QuickSort2Stabilizer(Key, indexFrom, indexTo) IdxL = LBound(Target, 2) IdxU = UBound(Target, 2) ReDim SWP(indexFrom To indexTo) For c = IdxL To IdxU For r = indexFrom To indexTo SWP(r) = Target(Key(0, r), c) Next For r = indexFrom To indexTo Target(r, c) = SWP(r) Next Next Else For r = indexFrom To indexTo Key(0, r) = r Key(1, r) = Target(OrderBy, r) Next Call QuickSort2Indexer(Key, indexFrom, indexTo, Desc) If Stable Then Call QuickSort2Stabilizer(Key, indexFrom, indexTo) IdxL = LBound(Target, 1) IdxU = UBound(Target, 1) ReDim SWP(indexFrom To indexTo) For r = IdxL To IdxU For c = indexFrom To indexTo SWP(c) = Target(r, Key(0, c)) Next For c = indexFrom To indexTo Target(r, c) = SWP(c) Next Next End If End Sub Private Sub QuickSort2Indexer(KeyList() As Variant, indexFrom As Long, indexTo As Long, Desc As Boolean) If IsSorted2(KeyList, 1, indexFrom, indexTo, Desc, False) Then Exit Sub Static LvA As Long, LvB As Long, LvLMT As Long Dim CurA As Long, CurB As Long Dim Pivot As Variant, SWP As Variant CurA = indexFrom CurB = indexTo Pivot = GetPivot2(KeyList, 1, indexFrom, indexTo, False) Do If Desc Then Do Until KeyList(1, CurA) <= Pivot CurA = CurA + 1 Loop Do Until KeyList(1, CurB) >= Pivot CurB = CurB - 1 Loop Else Do Until KeyList(1, CurA) >= Pivot CurA = CurA + 1 Loop Do Until KeyList(1, CurB) <= Pivot CurB = CurB - 1 Loop End If If CurA >= CurB Then Exit Do If KeyList(0, CurA) <> KeyList(0, CurB) Then SWP = KeyList(0, CurA) KeyList(0, CurA) = KeyList(0, CurB) KeyList(0, CurB) = SWP SWP = KeyList(1, CurA) KeyList(1, CurA) = KeyList(1, CurB) KeyList(1, CurB) = SWP CurA = CurA + 1 CurB = CurB - 1 End If Loop If LvA + LvB = 0 Then LvLMT = -Int(-(Log(indexTo - indexFrom + 1) / Log(2))) If indexFrom < CurA - 1 Then LvA = LvA + 1 If LvA > LvLMT Then Call MergeSort2Indexer(KeyList, indexFrom, CurA - 1, Desc) Else Call QuickSort2Indexer(KeyList, indexFrom, CurA - 1, Desc) End If LvA = LvA - 1 End If If indexTo > CurB + 1 Then LvB = LvB + 1 If LvB > LvLMT Then Call MergeSort2Indexer(KeyList, CurB + 1, indexTo, Desc) Else Call QuickSort2Indexer(KeyList, CurB + 1, indexTo, Desc) End If LvB = LvB - 1 End If If LvA + LvB = 0 Then LvLMT = 0 End Sub Private Sub QuickSort2Stabilizer(KeyList() As Variant, indexFrom As Long, indexTo As Long) Dim CurA As Long, CurB As Long, CurVal As Variant, i As Long CurA = indexFrom CurVal = KeyList(1, CurA) For i = indexFrom + 1 To indexTo If KeyList(1, i) = CurVal Then CurB = i Else If CurB > CurA Then Call QuickSort2(KeyList, 0, CurA, CurB) CurA = i CurVal = KeyList(1, i) End If Next If CurB > CurA Then Call QuickSort2(KeyList, 0, CurA, CurB) End Sub Private Function GetPivot2(Target() As Variant, OrderBy As Long, indexFrom As Long, indexTo As Long, FirstDim As Boolean) As Variant Const PIVOT_SIZE As Long = 9 If indexTo - indexFrom + 1 < PIVOT_SIZE * 3 Then If FirstDim Then GetPivot2 = Target((indexFrom + indexTo) \ 2, OrderBy) Else GetPivot2 = Target(OrderBy, (indexFrom + indexTo) \ 2) End If Else Dim Ary(0 To PIVOT_SIZE - 1), i As Long For i = 0 To PIVOT_SIZE - 1 If FirstDim Then Ary(i) = Target(Int((indexTo - indexFrom) / (PIVOT_SIZE - 1) * i) + indexFrom, OrderBy) Else Ary(i) = Target(OrderBy, Int((indexTo - indexFrom) / (PIVOT_SIZE - 1) * i) + indexFrom) End If Next Call InsertionSort(Ary, 0, PIVOT_SIZE - 1) GetPivot2 = Ary((PIVOT_SIZE - 1) \ 2) End If End Function
Rem マージソート2次元配列用 ================================================================================= Public Sub MergeSort2(Target() As Variant, OrderBy As Long, indexFrom As Long, indexTo As Long _ , Optional Desc As Boolean, Optional FirstDim As Boolean) Rem オプション ********************************** Rem Decs --------- 降順に並べる Rem FirstDim ----- 第1次元をソートする Rem ********************************************* Rem 対象区間が既にソート済みだったら何もしないで抜ける If IsSorted2(Target, OrderBy, indexFrom, indexTo, Desc, FirstDim) Then Exit Sub Dim IdxL As Long, IdxU As Long If FirstDim Then IdxL = LBound(Target, 2) IdxU = UBound(Target, 2) Else IdxL = LBound(Target, 1) IdxU = UBound(Target, 1) End If Rem ソートしない方の次元のサイズが大きい(4以上)場合は外部スワップに切り換え If (IdxU - IdxL + 1 >= 4) Then Call MergeSort2Ext(Target, OrderBy, indexFrom, indexTo, Desc, FirstDim) Exit Sub End If Rem 以下内部スワップ版の処理 _________________________________________________________________________________ Dim EndOfA As Long, StartOfB As Long EndOfA = indexFrom + Int((indexTo - indexFrom) / 2) StartOfB = EndOfA + 1 If EndOfA > indexFrom Then Call MergeSort2(Target, OrderBy, indexFrom, EndOfA, Desc, FirstDim) If StartOfB < indexTo Then Call MergeSort2(Target, OrderBy, StartOfB, indexTo, Desc, FirstDim) Dim SWP() As Variant Dim i As Long, j As Long Dim CurA As Long, CurB As Long Dim SortFlg As Boolean If FirstDim Then ReDim SWP(indexFrom To indexTo, IdxL To IdxU) CurA = indexFrom CurB = StartOfB For i = indexFrom To indexTo If CurA > EndOfA Then For j = IdxL To IdxU SWP(i, j) = Target(CurB, j) Next CurB = CurB + 1 ElseIf CurB > indexTo Then For j = IdxL To IdxU SWP(i, j) = Target(CurA, j) Next CurA = CurA + 1 Else If Desc Then SortFlg = Target(CurA, OrderBy) >= Target(CurB, OrderBy) Else SortFlg = Target(CurA, OrderBy) <= Target(CurB, OrderBy) End If If SortFlg Then For j = IdxL To IdxU SWP(i, j) = Target(CurA, j) Next CurA = CurA + 1 Else For j = IdxL To IdxU SWP(i, j) = Target(CurB, j) Next CurB = CurB + 1 End If End If Next For j = IdxL To IdxU For i = indexFrom To indexTo Target(i, j) = SWP(i, j) Next Next Else ReDim SWP(IdxL To IdxU, indexFrom To indexTo) CurA = indexFrom CurB = StartOfB For i = indexFrom To indexTo If CurA > EndOfA Then For j = IdxL To IdxU SWP(j, i) = Target(j, CurB) Next CurB = CurB + 1 ElseIf CurB > indexTo Then For j = IdxL To IdxU SWP(j, i) = Target(j, CurA) Next CurA = CurA + 1 Else If Desc Then SortFlg = Target(OrderBy, CurA) >= Target(OrderBy, CurB) Else SortFlg = Target(OrderBy, CurA) <= Target(OrderBy, CurB) End If If SortFlg Then For j = IdxL To IdxU SWP(j, i) = Target(j, CurA) Next CurA = CurA + 1 Else For j = IdxL To IdxU SWP(j, i) = Target(j, CurB) Next CurB = CurB + 1 End If End If Next For j = IdxL To IdxU For i = indexFrom To indexTo Target(j, i) = SWP(j, i) Next Next End If End Sub Rem マージソート2次元配列用(外部スワップ) =================================================================== Private Sub MergeSort2Ext(Target() As Variant, OrderBy As Long, indexFrom As Long, indexTo As Long _ , Desc As Boolean, FirstDim As Boolean) Dim SWP() As Variant, IdxL As Long, IdxU As Long Dim Key() As Variant, r As Long, c As Long ReDim Key(0 To 1, indexFrom To indexTo) If FirstDim Then For r = indexFrom To indexTo Key(0, r) = r Key(1, r) = Target(r, OrderBy) Next Call MergeSort2Indexer(Key, indexFrom, indexTo, Desc) IdxL = LBound(Target, 2) IdxU = UBound(Target, 2) ReDim SWP(indexFrom To indexTo) For c = IdxL To IdxU For r = indexFrom To indexTo SWP(r) = Target(Key(0, r), c) Next For r = indexFrom To indexTo Target(r, c) = SWP(r) Next Next Else For r = indexFrom To indexTo Key(0, r) = r Key(1, r) = Target(OrderBy, r) Next Call MergeSort2Indexer(Key, indexFrom, indexTo, Desc) IdxL = LBound(Target, 1) IdxU = UBound(Target, 1) ReDim SWP(indexFrom To indexTo) For r = IdxL To IdxU For c = indexFrom To indexTo SWP(c) = Target(r, Key(0, c)) Next For c = indexFrom To indexTo Target(r, c) = SWP(c) Next Next End If End Sub Private Sub MergeSort2Indexer(KeyList(), indexFrom As Long, indexTo As Long, Desc As Boolean) If IsSorted2(KeyList, 1, indexFrom, indexTo, Desc, False) Then Exit Sub Dim EndOfA As Long, StartOfB As Long EndOfA = indexFrom + Int((indexTo - indexFrom) / 2) StartOfB = EndOfA + 1 If EndOfA > indexFrom Then Call MergeSort2Indexer(KeyList, indexFrom, EndOfA, Desc) If StartOfB < indexTo Then Call MergeSort2Indexer(KeyList, StartOfB, indexTo, Desc) Dim SWP() As Variant Dim i As Long Dim CurA As Long, CurB As Long Dim SortFlg As Boolean ReDim SWP(0 To 1, indexFrom To indexTo) CurA = indexFrom CurB = StartOfB For i = indexFrom To indexTo If CurA > EndOfA Then SWP(0, i) = KeyList(0, CurB) SWP(1, i) = KeyList(1, CurB) CurB = CurB + 1 ElseIf CurB > indexTo Then SWP(0, i) = KeyList(0, CurA) SWP(1, i) = KeyList(1, CurA) CurA = CurA + 1 Else If Desc Then SortFlg = KeyList(1, CurA) >= KeyList(1, CurB) Else SortFlg = KeyList(1, CurA) <= KeyList(1, CurB) End If If SortFlg Then SWP(0, i) = KeyList(0, CurA) SWP(1, i) = KeyList(1, CurA) CurA = CurA + 1 Else SWP(0, i) = KeyList(0, CurB) SWP(1, i) = KeyList(1, CurB) CurB = CurB + 1 End If End If Next For i = indexFrom To indexTo KeyList(0, i) = SWP(0, i) KeyList(1, i) = SWP(1, i) Next End Sub
Rem マージソート1次元配列用 ←単体では絶対使うこと無いやろな ================================================ Public Sub MergeSort(Target(), indexFrom As Long, indexTo As Long, Optional Desc As Boolean) Rem オプション ********************************** Rem Decs --------- 降順に並べる Rem ********************************************* Rem 対象区間が既にソート済みだったら何もしないで抜ける If IsSorted(Target, indexFrom, indexTo, Desc) Then Exit Sub Dim EndOfA As Long, StartOfB As Long Rem 再帰処理で配列をAパートとBパートに2分割 EndOfA = indexFrom + Int((indexTo - indexFrom) / 2) StartOfB = EndOfA + 1 If EndOfA > indexFrom Then Call MergeSort(Target, indexFrom, EndOfA, Desc) If StartOfB < indexTo Then Call MergeSort(Target, StartOfB, indexTo, Desc) Rem 分割したAパートとBパートを比較してソート済み仮配列を作成 Dim SWP() As Variant Dim i As Long Dim CurA As Long, CurB As Long Dim SortFlg As Boolean ReDim SWP(indexFrom To indexTo) CurA = indexFrom CurB = StartOfB For i = indexFrom To indexTo If CurA > EndOfA Then 'Aパートが処理済ならBパートの残りを仮配列に書込み SWP(i) = Target(CurB) CurB = CurB + 1 ElseIf CurB > indexTo Then 'Bパートが処理済ならAパートの残りを仮配列に書込み SWP(i) = Target(CurA) CurA = CurA + 1 Else 'どちらも残ってたら比較して一方を仮配列に書込み If Desc Then '昇順降順の指定に従って判定 SortFlg = Target(CurA) >= Target(CurB) Else SortFlg = Target(CurA) <= Target(CurB) End If If SortFlg Then '判定結果に基づき、一方を仮配列に書込み SWP(i) = Target(CurA) CurA = CurA + 1 Else SWP(i) = Target(CurB) CurB = CurB + 1 End If End If Next Rem 仮配列をそのまま元配列に転記 For i = indexFrom To indexTo Target(i) = SWP(i) Next End Sub
Rem 挿入ソート1次元配列用 =================================================================================== Public Sub InsertionSort(Target() As Variant, indexFrom As Long, indexTo As Long, Optional Desc As Boolean) Rem オプション ********************************** Rem Decs --------- 降順に並べる Rem ********************************************* Dim CurA As Long, CurB As Long Dim SWP As Variant, Flg As Boolean For CurA = indexFrom + 1 To indexTo SWP = Target(CurA) If Desc Then Flg = (Target(CurA - 1) < SWP) Else Flg = (Target(CurA - 1) > SWP) End If If Flg Then CurB = CurA Do If Desc Then If Target(CurB - 1) >= SWP Then Exit Do Else If Target(CurB - 1) <= SWP Then Exit Do End If Target(CurB) = Target(CurB - 1) CurB = CurB - 1 Loop While CurB > indexFrom Target(CurB) = SWP End If Next End Sub Rem 配列が整列済みか否かのチェック関数1次元配列用 =========================================================== Private Function IsSorted(Target() As Variant, indexFrom As Long, indexTo As Long, Desc As Boolean) As Boolean Dim i As Long If Desc Then For i = indexFrom To indexTo - 1 If Target(i) < Target(i + 1) Then Exit Function Next Else For i = indexFrom To indexTo - 1 If Target(i) > Target(i + 1) Then Exit Function Next End If IsSorted = True End Function Rem 配列が整列済みか否かのチェック関数2次元配列用 =========================================================== Private Function IsSorted2(Target() As Variant, OrderBy As Long, indexFrom As Long, indexTo As Long, _ Desc As Boolean, FirstDim As Boolean) As Boolean Dim i As Long If FirstDim Then If Desc Then For i = indexFrom To indexTo - 1 If Target(i, OrderBy) < Target(i + 1, OrderBy) Then Exit Function Next Else For i = indexFrom To indexTo - 1 If Target(i, OrderBy) > Target(i + 1, OrderBy) Then Exit Function Next End If Else If Desc Then For i = indexFrom To indexTo - 1 If Target(OrderBy, i) < Target(OrderBy, i + 1) Then Exit Function Next Else For i = indexFrom To indexTo - 1 If Target(OrderBy, i) > Target(OrderBy, i + 1) Then Exit Function Next End If End If IsSorted2 = True End Function
(中途B) 2017/07/25(火) 22:30
>「今回の件以外の場面ではソートメソッドの方がいいよね」だと解釈して、もちろんYesです。 はい、認識の通りです。
>>こんな使い方をする時ってどんな時なんですか? >今回のは、元データを複数のテキストから読み込み、 〜 >それを元々すべてシート上で行っていたのが、過去何世代かの改良の中で処理時間短縮を狙って >徐々に配列内での処理割合を増やしていった経緯を持ってるヤツなんですね。 この件ですけど、ちょっと本題からは外れますが、ソート自体必要無いかも知れませんね? [[20170723091359]] 『配列 の使い方、Dictionaryの使い方』(tata) 此方で稲葉さんが今ちょうど解説されてますけど、Dictionaryを使えばソートしなくても集計出来るので 配列ソートやしーとに書き出してのソートなどの工程自体が省略できれば、今よりも早くなるかも知れませんね。 お二人の争いは主義の違い的な部分があるので、今回の件では下手すれば一生分かり合えないかも知れませんが、 争いの種は無くなるかも知れませんよ。
質問が今より早いコードに出来るか?と言う事でしたら、一度現在のコードと、元のデータと最終の結果の レイアウトを提示して頂ければ、もしかしたらアドバイスも出来たかも知れませんが。
今回の件では、もうコードを書いていて、ご自身の業務に支障の出ないだけの検証も充分してると言うのでしたら、 業務内容の説明もこれまでの経緯も、ここまでの話の流れでは分からないので、一般的にはソートメソッドの方が 便利ですよくらいしか言えないですねぇ。。。
喧嘩の原因も背景が分からない以上、中途Bさんの意見だけ聞いても判断できないのと、 主義の違いが根本原因な気もするので、そうであれば分かり合う事は難しいので、 後は先輩を立てるか、自身の正義を貫くかの選択になるんじゃないかと言う気がします。
書いてる間にコードは提示されてますね。 これはソート部分だけのコードですか?
(sy) 2017/07/25(火) 22:37
コード拝見しました。一目見て、私は使い方すら理解できませんでした! すごいですね。 これを理解して使いこなせるレベルの人の入社が約束されているんですね。 羨ましいです。 弊社は数式さえ怪しい人のオンパレードですよ。
一分一秒が惜しい業務に置かれたことないので、想像でしかありませんが ここまで書くなら、まずはハイスペックなパソコンを買ってもらいましょう! 勿体ないです。 ハイスペックな人材雇うより安いですっていえば一発です!
使い方も教えてもらっていいですか? 優先順位は A列昇順、漢字込 B列降順、数値 C列昇順、英数、半角全角大文字小文字区別しない、 みたいなのもできますか? (稲葉) 2017/07/25(火) 23:03
1次元の QuickSort 試させて頂きました。
1〜100万までの100万件のランダムデータを作成して昇順で実行しました。 1回目 : 4.78秒 2回目 : 4.81秒 3回目 : 4.81秒 4回目 : 4.79秒 5回目 : 4.78秒 6回目 : 4.78秒 7回目 : 4.78秒 8回目 : 4.78秒 9回目 : 4.75秒 10回目 : 4.79秒 平均 : 4.79秒
凄いですね。
ただ全く同じデータ(並び順も同じ)をシートに書き出して、ソートメソッドでソート後、配列に書き戻しました。 1回目 : 4.08秒 2回目 : 4.11秒 3回目 : 4.13秒 4回目 : 4.13秒 5回目 : 4.13秒 6回目 : 4.13秒 7回目 : 4.20秒 8回目 : 4.16秒 9回目 : 4.26秒 10回目 : 4.23秒 平均 : 4.16秒
中途Bさん作成の QuickSort は純粋に凄いと言う感想しかありませんが、 ソートメソッドの方が遅いと言うのは、どう言うコードで検証されたんでしょうか? ソート用のシートなどは一々追加しなくても、それ用のまっさらなシートを一つマクロブックに 置いておけば良いので、シートに書出し→ソート→書き戻しの手順だけで良いですよね。
因みに私が検証したテストコードは以下です。 A列に1〜100万までの整数データを、1行目から100万行目まで項目は無しで作成しました。
Sub 配列ソートtest() Dim v As Variant Dim v2() As Variant Dim i As Long Dim k As Long Dim t As Double
For k = 1 To 10 '事前の配列作成 v = Range("A1").CurrentRegion.Value ReDim v2(1 To UBound(v, 1)) For i = 1 To UBound(v, 1) v2(i) = v(i, 1) Next i
'計測はここから t = Timer Call QuickSort(v2, 1, UBound(v)) Debug.Print " " & k & "回目 : " & WorksheetFunction.Round(Timer - t, 2) & "秒" 'ここまで
Next k
'ソート結果の確認用にシートに書出し For i = 1 To UBound(v2) v(i, 1) = v2(i) Next i Range("C1").Resize(UBound(v2)).Value = v
End Sub
Sub シート書出しtest() Dim v As Variant Dim v2() As Variant Dim v3() As Variant Dim i As Long Dim k As Long Dim t As Double
For k = 1 To 10 '事前の配列作成 v = Range("A1").CurrentRegion.Value ReDim v2(1 To UBound(v, 1)) For i = 1 To UBound(v, 1) v2(i) = v(i, 1) Next i
'計測はここから t = Timer ReDim v3(1 To UBound(v2), 0) For i = 1 To UBound(v2) v3(i, 0) = v2(i) Next i Sheets("Sheet1").Range("C:C").ClearContents Range("C1").Resize(UBound(v2)).Value = v3
With Sheets("Sheet1").Sort .SortFields.Clear .SortFields.Add Key:=Range("C1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal .SetRange Range("C1").CurrentRegion .Header = xlNo .Apply End With
v = Range("C1").CurrentRegion.Value ReDim v2(1 To UBound(v, 1)) For i = 1 To UBound(v, 1) v2(i) = v(i, 1) Next i Debug.Print " " & k & "回目 : " & WorksheetFunction.Round(Timer - t, 2) & "秒" 'ここまで
Next k
'メソッド版は確認用の記述は必要無し
End Sub
(sy) 2017/07/26(水) 02:16
半分寝てたので、シート装飾してたりしてなかったり無茶苦茶ですね、 すいません。
でも速度は変わらないので、他のは試してないので分かりませんが、ソートメソッドの方が遅いと言うのは、 もう一度ちゃんと検証してから主張した方が良いかも知れませんね。
(sy) 2017/07/26(水) 02:27
そか、あまり興味がないテーマなので考慮してませんでしたが、
そもそも、なんでエクセルにソートさせたら速いの?って話ですよね。
VBAで大量にループするだけで処理速度的には不利ですよね?
C言語とかでソートする部分を書いて、それをVBAで使うようにすると、
処理速度があがりますよね?
逆に「自前に拘る&処理速度に拘る」ならそれくらいしてもいいのでは?
ぼく程度のレベルなら、安心・安全が気が楽でいいです。
(あと、クイックソートより速いアルゴリズムがあった気がしましたが、
見つかりませんね。。。ぼくの勘違いか^^;)
(まっつわん) 2017/07/26(水) 06:54
2次元の QuickSort2 MergeSort2 も試してみました。 1次元の MergeSort も試しましたが100万件では遅すぎるので途中で止めました。
これなぜ2次元目がデータ数なんですか? 1次元目の方が扱いやすいと思うのですが? (シートとの連携と言う意味ですけど)
後各次元の中身の始まりは0からなので、1からしか受け付けないのはどうかと思います。
これもソートメソッドの方が早いですね。 2次元側がデータなので、シートソートの方が並べ替え分不利ですけど、提示の条件のままの配列で検証しています。
QuickSort2 データ数20万件 1回目 : 1.09秒 2回目 : 1.09秒 3回目 : 1.08秒 4回目 : 1.09秒 5回目 : 1.09秒 6回目 : 1.08秒 7回目 : 1.08秒 8回目 : 1.09秒 9回目 : 1.06秒 10回目 : 1.09秒 平均 : 1.08秒
MergeSort2 データ数20万件 1回目 : 1.91秒 2回目 : 1.92秒 3回目 : 1.97秒 4回目 : 1.92秒 5回目 : 1.91秒 6回目 : 1.91秒 7回目 : 1.9秒 8回目 : 1.89秒 9回目 : 1.89秒 10回目 : 1.91秒 平均 : 1.91秒
シートでソート データ数20万件 1回目 : 0.83秒 2回目 : 0.83秒 3回目 : 0.83秒 4回目 : 0.85秒 5回目 : 0.81秒 6回目 : 0.88秒 7回目 : 0.89秒 8回目 : 0.84秒 9回目 : 0.83秒 10回目 : 0.86秒 平均 : 0.85秒
2次元配列で、1次元目は1つ、2次元目をデータで配列を作りました。 中身のデータは1次元配列の時と同じです。 100万件だと遅すぎるので20万件で検証しました。
以下今回の検証コードです。 MergeSort2 も呼出先が変わるだけです。
Sub 配列ソートtest2次元() Dim v As Variant Dim v2() As Variant Dim v3() As Variant Dim i As Long Dim k As Long Dim t As Double
For k = 1 To 10 '事前の配列作成 v = Sheets("Sheet1").Range("A1").CurrentRegion.Value ReDim v2(1 To 1, 1 To UBound(v, 1)) For i = 1 To UBound(v, 1) v2(1, i) = v(i, 1) Next i
'計測はここから t = Timer Call MergeSort2(v2, 1, 1, UBound(v)) Debug.Print " " & k & "回目 : " & WorksheetFunction.Round(Timer - t, 2) & "秒" 'ここまで
Next k
'ソート結果の確認用にシートに書出し ReDim v3(1 To UBound(v, 1), 0) For i = 1 To UBound(v, 1) v3(i, 0) = v2(1, i) Next i Sheets("Sheet1").Range("C:C").ClearContents Sheets("Sheet1").Range("C1").Resize(UBound(v3, 1)).Value = v3
End Sub
Sub シートソートtest2次元() Dim v As Variant Dim v2() As Variant Dim v3() As Variant Dim i As Long Dim k As Long Dim t As Double
For k = 1 To 10 '事前の配列作成 v = Sheets("Sheet1").Range("A1").CurrentRegion.Value ReDim v2(0, 1 To UBound(v, 1)) For i = 1 To UBound(v, 1) v2(0, i) = v(i, 1) Next i
'計測はここから t = Timer ReDim v3(1 To UBound(v2, 2), 0) For i = 1 To UBound(v2, 2) v3(i, 0) = v2(0, i) Next i Sheets("Sheet1").Range("C:C").ClearContents Sheets("Sheet1").Range("C1").Resize(UBound(v, 1)).Value = v3
With Sheets("Sheet1").Sort .SortFields.Clear .SortFields.Add Key:=Sheets("Sheet1").Range("C1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal .SetRange Sheets("Sheet1").Range("C1").CurrentRegion .Header = xlNo .Apply End With
v = Sheets("Sheet1").Range("C1").CurrentRegion.Value For i = 1 To UBound(v, 1) v2(0, i) = v(i, 1) Next i Debug.Print " " & k & "回目 : " & WorksheetFunction.Round(Timer - t, 2) & "秒" 'ここまで
Next k
'メソッド版は確認用の記述は必要無し
End Sub
(sy) 2017/07/26(水) 07:25
現状は機能変更等はご自身で行うので、クイックソートの運用も問題無い、とお考えのようですが、10年後、20年後でも貴方は同じ会社で同じ業務を行っているとは限らないので、判りやすさや直しやすさは大事です。 長くやっている人ほど、いつまでも自分自身がコード修正するものではない、という事を実感しているので、先輩は独自ロジックを感覚的に避けるようになっているのかもですね。
そして、徹底的に速度追求するならば、同じロジックをC言語等のコンパイラ言語で書いたら、もっと速いのではないかと思います。 Excelマクロのメリットは、入力データや出力データを見易い表の形で用意できる事だと思います。 Cで書いても、表に転記するところまで含めてExcelの内部処理と比較することになり、ソート時間より転記時間の方が大きくて不利そうですが、例えばDLLにしてExcelから呼び出す関数にしてしまえば、現状のクイックソートロジックよりは速くなる事でしょう。とはいえ、速くなると予想できはしますが、作成にかかる工数と向上する速度で得られる時間を考えると、C言語等によるDLL化は手を出さなくても良いと思います。
あと、クイックソートした結果の検証は、どのように行いましたか? おそらく、Excelのソート結果と比較する数式でも書いたのかと思いますが、それはExcelの処理を全面信用し、自身のコードにはバグがある可能性を想定したものかと思います。 この信頼性の違いも、大事な判断材料になるでしょう。
(???) 2017/07/26(水) 09:29
syさん、再検証までして頂いておいてすみません。
多分私がどこか見落しとか早とちりしてるんでしょう。
とりあえず自分の検証コードだけは貼っておかないとさすがにヤバいそうなので、
それだけ貼っておきます。
Option Explicit
Public Declare Function timeGetTime Lib "winmm.dll" () As Long Public Declare Function timeBeginPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long Public Declare Function timeEndPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long
Sub 検証6() Const RowSize As Long = &H10000 Const ColSize As Long = 3 Const OrderIdx As Long = 2 Const Descend As Boolean = False Dim Ra As Range, vOrg() As Variant, vTest() As Variant Dim r As Long, c As Long Dim t As Long, t2 As Long
Set Ra = Range(Cells(1, 1), Cells(RowSize, ColSize))
ReDim vOrg(1 To RowSize, 1 To ColSize) For r = 1 To RowSize vOrg(r, 1) = r For c = 2 To ColSize vOrg(r, c) = Int(Rnd() * RowSize) Next Next Debug.Print Format$(RowSize, "#,##0") & " x " & ColSize Ra.Resize(, 3).Value = vOrg 'ColSizeの指定を無視して3列だけ
Call timeBeginPeriod(1) Rem クイックソート vTest = vOrg t = timeGetTime() QuickSort2 vTest, OrderIdx, 1, RowSize, Descend, True Debug.Print timeGetTime() - t, "クイックソート" Ra.Offset(0, 3).Resize(, 3).Value = vTest Rem クイックソート(安定版) vTest = vOrg t = timeGetTime() QuickSort2 vTest, OrderIdx, 1, RowSize, Descend, True, True Debug.Print timeGetTime() - t, "クイックソート(安定版)" Ra.Offset(0, 6).Resize(, 3).Value = vTest Rem シート上で並べ替え vTest = vOrg t = timeGetTime() Application.ScreenUpdating = False ' t2 = timeGetTime() Workbooks.Add ' Debug.Print timeGetTime() - t2, " 内)シート追加" With Cells(1, 1).Resize(RowSize, ColSize) ' t2 = timeGetTime() .Value = vTest ' Debug.Print timeGetTime() - t2, " 内) 配列貼付" ' t2 = timeGetTime() .Sort .Columns(OrderIdx), IIf(Descend, xlDescending, xlAscending) ' Debug.Print timeGetTime() - t2, " 内)Sort実行" ' t2 = timeGetTime() vTest = .Value ' Debug.Print timeGetTime() - t2, " 内)配列再読込" End With ' t2 = timeGetTime() ActiveWorkbook.Close False ' Debug.Print timeGetTime() - t2, " 内)シート破棄" Application.ScreenUpdating = True Debug.Print timeGetTime() - t, "シート上で並べ替え" Ra.Offset(0, 9).Resize(, 3).Value = vTest Rem マージソート vTest = vOrg t = timeGetTime() MergeSort2 vTest, OrderIdx, 1, RowSize, Descend, True Debug.Print timeGetTime() - t, "マージソート" Ra.Offset(0, 12).Resize(, 3).Value = vTest
Call timeEndPeriod(1) End Sub
↓Print結果 65,536 x 3 154 クイックソート 173 クイックソート(安定版) 421 シート上で並べ替え 389 マージソート
(中途B) 2017/07/26(水) 12:20
なんかスピード的な流れになっていますが、 単純なSortならどちらも大差ないでしょう。
>複雑(数値文字混合等)なデータを複数列に対して的確なソート(昇順・降順)が出来て、
これをコードでGeneralizeするのは無理でしょう。
>尚且つ後継者がそれを継承できるなら...
QuickSort・BubleSort等をを使用するにしても上記を満足させるにはむしろ 的確なソート結果をもたらすデータの作りこみが大事なのだと思いますよ?
( seiya) 2017/07/26(水) 13:05
ちょっと思ったので、ひとつだけ。
ひょっとしてSortメソッドよりSortオブジェクト使った方が、
複数フィールドを相手にする場合に、もの凄く優位なのではないか? と。
そんなの当たり前? ですか?
(中途B) 2017/07/26(水) 18:01
>そんなの当たり前? ですか? これ私に対するコメント?
そんなこと言ってないし、各結果の同一値内の他列の値が微妙に違ってるんですよね。
この程度の誤差は許容範囲なのであれば私のレスは無視してください。 ( seiya) 2017/07/26(水) 19:07
>多分私がどこか見落しとか早とちりしてるんでしょう。 早とちりと言うか、ご自身のコードに関しては思い入れもあるでしょうから、しっかりと作り込まれているし、 検証コードも最適なコードになっていると思いますが、シートソートの検証コードは正直に申し上げると最適化 とは程遠い雑なコードになっています。 おそらくシートソートは遅いから無駄との偏見からくる投げやりなコード、早く処理する為の検討をしていない としか思えないコードになっています。
上でも書きましたが、 >ソート用のシートなどは一々追加しなくても、それ用のまっさらなシートを一つマクロブックに >置いておけば良いので、シートに書出し→ソート→書き戻しの手順だけで良いですよね。 一時作業ブックの追加も同じ事です。 ブックの追加などは非常に重たい処理なので、記述すれば遅くなって当然です。 マクロブックは必ず開いているんですから、そこに作業シートを予め用意すれば、わざわざ別に一時ファイル など用意しなくても良いですよね。
あくまで目的は、 1、配列内のデータを早くソートしたい。 2、本来の目的は集計を早く処理したい。 ですよね? なら元が配列内データをソートして配列に戻すと言う事が満足出来れば、良いと言う事ですよね?
マクロブックにシートを予め用意するのは、それらの邪魔になるんですか? 私は一切影響しないと思います。 また見た目余計なシートがあるのが嫌だとかあるなら非表示にすれば良い事ですし、 しかも非表示の方が僅かに早いですしね。
それらを反映させたのが私のテストコードです。
以下の遅くするだけの記述は全く無くても問題ないはずです。 >Workbooks.Add >ActiveWorkbook.Close False >Application.ScreenUpdating = False ↑これもソートの速度を落とす原因になります。 これするなら、初めからシートを非表示にしておけば、画面の更新が行われないので同じ結果になります。 僅かですけど記述自自体の実行時間が無駄になります。
配列ソートのコード自体は素晴らしいと思いますが、それでもシートソートには、 速度(これは微々たる差と思います)・記述の簡単さ・メンテナンス性・引継ぎの容易さ・正確性を担保する為の検証時間、 どれをとっても敵わないと思います。 先輩と対決する前に、もう一度じっくり考え直す事をお勧めします。
(sy) 2017/07/26(水) 22:41
syさん、検証ありがとうございました。 その前に > 、Dictionaryを使えばソートしなくても集計出来るので こちらの件ですが、 実はだいぶ初期の段階で取り入れていた形跡が残ってました。 早々に切り捨てられていた様ですが、再度検討してみた方が良さそうですね。 おそらく集計結果をシートに出力した後のレポートとしての体裁を整えるのに 何かしら不都合があって、方向性を切り替えたのではないかと思います。 そっちを上手く処理できれば、良い結果が得られそうな気がします。
> 1次元目の方が扱いやすいと思うのですが? > (シートとの連携と言う意味ですけど) 引数FirstDim:=Trueで1次元目の方をソート対象に切り替える仕様です。
> 後各次元の中身の始まりは0からなので、1からしか受け付けないのはどうかと思います。 すみません。よくわかりません。 まだアップして頂いた検証コード試せてないもので。 後ほど見直してみます。
稲葉さんもありがとうございました。 > 優先順位は > A列昇順、漢字込 > B列降順、数値 > C列昇順、英数、半角全角大文字小文字区別しない、 > みたいなのもできますか? いやいや、全然ダメダメです。 え? StrComp使って判定? でもその前にデータ型判定は? とか考えたらやる気削がれますね。
はずかしい事に、私、Sortオブジェクトって使った事ないんですよ。 Sortメソッドばっか使ってました。 で複数フィールドをソートする場合の記述も、 優先順位の低い方から1回ずつSortメソッドを実行する という記述しかしませんでした。 なので、コードでやる場合も同様に 優先順位の低い方から1回ずつソートを実行すればいいじゃん って、何の疑問も持たずに今までやってきました。
まっつわんさん > C言語とかでソートする部分を書いて、それをVBAで使うようにする ははは^^; 入門書のインデックスだけ見て本棚で眠ってますね。 プライベートで頑張ります。
> クイックソートより速いアルゴリズムがあった気がしましたが、 鳩ノ巣ソートの事かも知れません http://fanblogs.jp/booboo7x70/archive/99/0 違ってたらすみません。
???さん > 自信を持ってください。 ありがとうございます。 先輩しか知らないので、他の人の評価なんて考えた事ありませんでした。
> コメントにREMを使う なんか先輩に叩きこまれました。 コメント書く時はRem。クォーテーションはコメントアウトだから使い分けなさい。 だそうです。内心どーでもいいと思いながら従ってます。
> 長くやっている人ほど、いつまでも自分自身がコード修正するものではない、という事を実感しているので、 > 先輩は独自ロジックを感覚的に避けるようになっているのかも どうやらそうみたいです。 というのも、 >> 私はソート1回での処理を思ってたんですが、先輩は4回ソートするつもりだったかもなんです。 について今日先輩に確認してみたんですが、そういった認識齟齬はありませんでした。 ではなぜクイックソートは却下なんですか? と訊いてみたら、なんか部内限定ではありますがオフィシャルにするみたいです。業務ツールとして。 なので、皆さんのアドバイス通りの事を考えてのご判断だったようです。
だったら最初からそう言って欲しかったですよ。もー って言ったら、どうやら「試されてた」みたいですね。 その辺の感覚はやっぱまだまだだね〜 って あ た り ま え だ !!
あ、でも私が組んだソートコードは「絶対残しとけよ」とも言われました。 はい。またどこかでエッセンスだけでも役に立つ機会があればそれでいいです。
それと >>そんなの当たり前? ですか? > これ私に対するコメント? は、皆さんへの問いかけでした。 syさんの検証コードを思い返して「もしや」と思って。 ダメだな 焦って書き込むもんじゃないですね。 後で自分で確認します。
なんかseiyaさんには最初っから謝ってばかりだった気がします。 いろいろとご意見頂き、ありがとうございました。
> 各結果の同一値内の他列の値が微妙に違ってる QuickSort2のことでしたら引数StableにTrueを指定しなければそうなります。 という話じゃなかったら、まぁ私のヘマですね。
・・・という事でした。 もうスレの主題としては終わりましたね。 これ以上脱線したまま伸ばす方が迷惑だと思いますので、締めさせて頂きます。 みなさん、ありがとうございました。
syさんの検証コードとの比較検証については自分でやります。 まぁ100%私のヘマでしょうから。 判明したら謝りに来ます。
(中途B) 2017/07/26(水) 22:48
syさん ありがとうございます。 書き込み中に更にお返事頂いたようで。
> 偏見からくる投げやりなコード、早く処理する為の検討をしていない > としか思えないコードになっています。 いやホントお恥ずかしい限りです。 ご指摘を踏まえて再度検証して参ります。
先輩とは対決にすらならなかったです^^; でもまあ、その方がじっくり検証できると思うのでよかったか。
前レスでも申しましたが、頂いたsyさんの検証コードをまだ試せていません。 証拠を取り揃えて、改めて「敗北宣言」に来ようと思います。
親身にお付き合い頂き、ありがとうございました。
(中途B) 2017/07/26(水) 23:03
REMと'を使い分けているのですね。 それは先輩のローカルルールですが、一理ありますので、従っておいて損はないです。 そういう細かい配慮は、言われて身につくものではないので、師として従ってよい方に思えます。 今は盲目的に従っておき、後で今回のような機会に、そういう意味だったのか!、と閃くのも、面白いものですよ。
REMについては、おそらくコードの可動性、メンテナンス性を考慮しての考えでしょう。REMと書いてあるのは処理説明であり、大事な情報。 シングルクォートは、デバッグのためや修正のために非実行にした行であり、なんだったら削除しても構わない情報。 この2つが一目で区別できます。 反対意見としては、インタプリンタなんだから、3文字より1文字の方が速い、という点ですが、近年の十分速いCPUならば、数文字くらい計測もできないくらいの差でしょう?、という事を考えての事かと思います。(数文字を気にするなら、変数名やコメントを無くすとか短くするとか、可読性が落ちる方向になっていきますから)
(???) 2017/07/27(木) 09:53
こんばんは。 敗北宣言に参りました。
その前に ???さん、追加コメントありがとうございました。 なるほど「受け継がれていくことを意識しなさい」という事ですね。 経理の仕事でも、伝票の摘要なんかで上司に「誰が見ても意味が分かるように書け」って言われます。 結局はこれも、Remの使い方も、皆さんにアドバイスされた事と本質的には同じ意味ですね。
では、今日再検証した結果を投下して、今夜はこのまま失礼させて頂きます。
●syさんに頂いた検証コードをそのままやった場合 データサイズ262144 x 1 (0以上1024未満の整数) シート クイック 安定クイック マージ 10回平均 0.87秒 0.25秒 0.76秒 1.05秒
データサイズ1048576 x 1 (0以上1048576未満の整数) シート クイック 安定クイック マージ 10回平均 2.68秒 2.10秒 2.96秒 4.62秒
●配列ソートはFirstDimを使い、シートソートも別シートでやった場合 データサイズ262144 x 1 (0以上1024未満の整数) シート クイック 安定クイック マージ 10回平均 0.60秒 0.25秒 0.76秒 1.05秒
データサイズ1048576 x 1 (0以上1048576未満の整数) シート クイック 安定クイック マージ 10回平均 2.51秒 2.10秒 2.95秒 4.68秒
▼2番目の検証に使用した改造コード(あらかじめSheet1のA列にデータを用意して実行)
Sub 配列ソートtest2次元_FirstDim() Dim v() As Variant Dim i As Long Dim k As Long Dim t As Double
For k = 1 To 10 v = Sheets("Sheet1").Range("A1").CurrentRegion.Value '計測はここから t = Timer ' Call QuickSort2(v, 1, 1, UBound(v), , True, False) Call QuickSort2(v, 1, 1, UBound(v), , True, True) ' Call MergeSort2(v, 1, 1, UBound(v), , True) Debug.Print " " & k & "回目 : " & WorksheetFunction.Round(Timer - t, 2) & "秒" 'ここまで Next k
'ソート結果の確認用にシートに書出し Sheets("Sheet1").Range("C:C").ClearContents Sheets("Sheet1").Range("C1").Resize(UBound(v, 1)).Value = v End Sub Sub シートソートtest2次元_別シートで() Dim v() As Variant Dim k As Long Dim t As Double
For k = 1 To 10 v = Sheets("Sheet1").Range("A1").CurrentRegion.Value '計測はここから t = Timer With Sheet2 .Range("A:A").ClearContents .Range("A1").Resize(UBound(v, 1)).Value = v End With With Sheet2.Sort .SortFields.Clear .SortFields.Add Key:=Sheet2.Range("A1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal .SetRange Sheet2.Range("A1").CurrentRegion .Header = xlNo .Apply End With v = Sheet2.Range("A1").CurrentRegion.Value Debug.Print " " & k & "回目 : " & WorksheetFunction.Round(Timer - t, 2) & "秒" 'ここまで Next k
'ソート結果の確認用にシートに書出し Sheets("Sheet1").Range("C:C").ClearContents Sheets("Sheet1").Range("C1").Resize(UBound(v, 1)).Value = v End Sub
●考察 syさんによる検証 (データ数20万件) QuickSort2 1.08秒 シートでソート 0.85秒 私によるsyさんの検証コードの再実施 (データ数262144) QuickSort2(安定) 0.76秒 シートでソート 0.87秒 改造したコードでの検証 QuickSort2(安定) 0.76秒 シートでソート 0.60秒
シート上ソートはsyさん結果と私の結果は同じ。 別シートでソートした検証では更に速くなった。 syさんのご指摘通り、別シートでのソートが最も速かった。
QuickSort2はsyさんの結果がシート上ソートより遅いのに対して 私の再実施結果はシート上ソートより速かった。この件に関しては、 使用したデータや環境がsyさんとでは異なるはずなので、違いの要因は不明だが、 裏を返せば「シート上ソートはデータ内容や環境に左右されにくい」という証明だ。
また1048576行の場合、データが1列とはいえ、不安定ソートのままシートに勝っても意味ないので 安定クイックと比較すると、やはり負けてしまう。
結論「負けました」
(中途B) 2017/07/27(木) 22:57
●複数列のデータ、及び複数回ソートでの検証 データは先頭行を見出しとして、2行目以下に262144行の乱数(0以上の整数)を配置 列数は1列、3列、5列、10列の4パターン データの内容は「乱数×4の列番乗」とした。(上位列になるほどデータの重複割合が多いデータ) ソート手段は ・クイックソート-----------不安定のまま下位列から1回ずつソート ・クイックソート(安定版)---引数Stable:=Trueとして下位列から1回ずつソート ・Sortオブジェクト---------別シートにデータを転記し、SortFieldを上位列からAddして一括Apply ・Sortメソッド-------------別シートにデータを転記し、Key1のみ使用して下位列から1回ずつソート 計測はそれぞれ10回行い、その平均をとった。
▼検証に使用したコード(Sheet1とSheet2を使用。Sheet2はあらかじめ非表示にした)
Public Declare Function timeGetTime Lib "winmm.dll" () As Long Public Declare Function timeBeginPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long Public Declare Function timeEndPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long
Sub 検証8Call() Const RowSize As Long = &H40000 Const ColSize As Long = 10 Const SortCount As Long = 10 '←1列目から何列目までソートするか Dim i As Long
Application.ScreenUpdating = False Sheet1.Cells.NumberFormat = "@" Call timeBeginPeriod(1) For i = 1 To 10 Debug.Print i & "回目", RowSize & " x " & ColSize Call 検証8(RowSize, ColSize, SortCount) Next Call timeEndPeriod(1) Sheet1.Cells.ClearFormats Application.ScreenUpdating = True End Sub Private Sub 検証8(RowSize As Long, ColSize As Long, OrderLMT As Long) Dim vTest() As Variant, vOrg() As Variant Dim r As Long, c As Long Dim t As Long, i As Long
ReDim vOrg(0 To RowSize, 1 To ColSize) For c = 1 To ColSize vOrg(0, c) = "乱数×" & 4 ^ c Next For r = 1 To RowSize For c = 1 To ColSize vOrg(r, c) = Int(Rnd() * 4 ^ c) Next Next Rem クイックソート vTest = vOrg t = timeGetTime() '------------------------------------------▼ココから For i = OrderLMT To 1 Step -1 QuickSort2 vTest, i, 1, RowSize, , True Next Debug.Print timeGetTime() - t, ",クイックソート" '-----------▲ココまで Sheet1.Cells(1, 1 + ColSize * 0).Resize(RowSize + 1, ColSize).Value = vTest Rem クイックソート(安定版) vTest = vOrg t = timeGetTime() '------------------------------------------▼ココから For i = OrderLMT To 1 Step -1 QuickSort2 vTest, i, 1, RowSize, , True, True Next Debug.Print timeGetTime() - t, ",クイックソート(安定版)" '---▲ココまで Sheet1.Cells(1, 1 + ColSize * 1).Resize(RowSize + 1, ColSize).Value = vTest Rem Sortオブジェクト vTest = vOrg With Sheet2 .Rows.Delete t = timeGetTime() '--------------------------------------▼ココから .Cells(1, 1).Resize(RowSize + 1, ColSize).Value = vTest With .Sort .SortFields.Clear For i = 1 To OrderLMT .SortFields.Add Key:=Sheet2.Columns(i) Next .SetRange Sheet2.UsedRange .Header = xlYes .Apply End With vTest = .Cells(1, 1).CurrentRegion.Value End With Debug.Print timeGetTime() - t, ",Sortオブジェクト" '---------▲ココまで Sheet1.Cells(1, 1 + ColSize * 2).Resize(RowSize + 1, ColSize).Value = vTest Rem Sortメソッド vTest = vOrg With Sheet2 .Rows.Delete t = timeGetTime() '--------------------------------------▼ココから With .Cells(1, 1).Resize(RowSize + 1, ColSize) .Value = vTest For i = OrderLMT To 1 Step -1 .Sort .Columns(i), Header:=xlYes Next End With vTest = .Cells(1, 1).CurrentRegion.Value End With Debug.Print timeGetTime() - t, ",Sortメソッド" '-------------▲ココまで Sheet1.Cells(1, 1 + ColSize * 3).Resize(RowSize + 1, ColSize).Value = vTest End Sub
●計測結果(秒) 1列目だけをソート_____|_1列__3列__5列__10列 クイックソート | 0.1 0.1 0.2 0.3 クイックソート(安定版)| 0.7 0.7 0.7 0.8 Sortオブジェクト | 0.6 1.3 2.2 4.1 Sortメソッド | 0.6 1.3 2.1 4.1
1〜3列目をソート______|_1列__3列__5列__10列 クイックソート | 0.5 0.9 1.3 クイックソート(安定版)| 2.2 2.4 2.7 Sortオブジェクト | 1.7 2.4 4.4 Sortメソッド | 2.0 3.1 5.9
1〜5列目をソート______|_1列__3列__5列__10列 クイックソート | 1.9 2.6 クイックソート(安定版)| 4.1 4.9 Sortオブジェクト | 2.5 4.5 Sortメソッド | 4.2 7.8
1〜10列目をソート_____|_1列__3列__5列__10列 クイックソート | 6.8 クイックソート(安定版)| 10.1 Sortオブジェクト | 4.6 Sortメソッド | 12.4
1列データの結果は先のsyさん検証コードを改造した結果と同じ。 1列目だけをソートする場合、列数が多いデータほど配列ソートが速くなった。
複数回ソート場合、クイックソート(不安定のまま)の結果は比較対象外として、 クイックソート(安定版)とSortメソッドの比較では配列ソートが速くなった。 但し、Sortメソッドの使い方が「Key1のみ使用で下位列から1回ずつソート」だから 機能制限を課しており、フェアな比較とは言えない。 Sortメソッドで1回あたり複数Keyを使用すれば配列ソートは負けるのではないか?(要確認)
Sortオブジェクトに関しては >>複数フィールドを相手にする場合に、もの凄く優位なのではないか? との予想の通りの結果だった。 SortFieldの数が増えても処理時間はほとんど増えてない。 これはすごい。KO負けだ。
3列データの3回ソートでも負けてるし、 結局勝ってるのは5列以上のデータの3回ソート迄か。
結論「負けました」
今日は以上です。 では、失礼します。
(中途B) 2017/07/27(木) 22:59
これで最後です
●>>Sortメソッドで1回あたり複数Keyを使用すれば配列ソートは負けるのではないか?(要確認) について一応追加確認してみた。
1〜3列目をソート______|_1列__3列__5列__10列 クイックソート(安定版)| 2.3 2.4 2.8 Sortオブジェクト | 1.6 2.4 4.4 Sortメソッド | 1.6 2.4 4.5 ←20%〜23%削減(ソート回数が2回分減った効果)
1〜5列目をソート______|_1列__3列__5列__10列 クイックソート(安定版)| 4.3 4.9 Sortオブジェクト | 2.5 4.6 Sortメソッド | 3.1 5.6 ←26%〜28%削減(ソート回数が3回分減った効果)
1〜10列目をソート_____|_1列__3列__5列__10列 クイックソート(安定版)| 10.3 Sortオブジェクト | 4.6 Sortメソッド | 7.5 ←およそ40%削減(ソート回数が6回分減った効果)
思った通りだった。
●SortオブジェクトがSortFieldの数が増えても高速なのは、昇順・降順混在でもそうなのか確認 仮に10列あるSortFieldをすべて昇順でソートするなら、10列のキーを連結して比較すれば、 実質的に1回のソートで済む。先の検証はその結果だったのでは? ・・・などという安易な仮説が正しければ、 昇順と降順を交互に指示する場合は、その「キーを連結して比較」とやらができず、 実質ソート回数が増えて多少遅くなるはずだ。 可能性の排除の意味で確認してみる。
先の検証と同じく262144行 x 10行の乱数データを使用した1〜10列目をソートするコードを流用し、 偶数列の並び順のみ降順指定した。(乱数の内容は、全列とも0以上16未満の整数にした)
1〜10列目をソート_____|10列 クイックソート(安定版)| 9.7 Sortオブジェクト | 4.6 Sortメソッド | 7.9
やはり変わらなかった。
以上で終わります。 途中からは検証そのものを楽しんでしまってる節があり、自らスレ汚しだったかもしれません。 とりあえず手持ちのコードでSortメソッド使ってる部分は すべてSortオブジェクトに書き換えなければ、と感じています。 またプライベートでArrayListやADOも交えて比較してみたいなと思います。
大変お世話にました。ありがとうございました。
(中途B) 2017/07/28(金) 15:20
そして、この程度の差なら標準機能のSortだよね、という先輩の意見も適していたでしょうし、インタプリンタはやっぱり不利だよね、という事も確認できました。
いずれDBを試す際は、インデックスキーの有無に気をつけてください。 インデックスを付けると格段に性能向上しますが、DB領域も使用するので、さじ加減は設計者に左右されるのですよ。 遅い、遅いと言っていたシステムが、話を聞くと、インデックス?、なにそれ?、とかもありがちです。 頑張ってください。
(???) 2017/07/28(金) 15:43
>> 後各次元の中身の始まりは0からなので、1からしか受け付けないのはどうかと思います。 >すみません。よくわかりません。
最後これだけ、 LBOUNDが0の時はエラーになりますね。と言う事です。 例えばv(0 to 100) とかv(0 to 100 , 0) と言った配列は扱えないですね。と言う事です。
ファイルシステムオブジェクトの ReadAll などで読み込んだデータを Split関数 で分割して Variant型変数 に一括取り込みしたような配列や、 ADO の GetRowsメソッド などでAccessなどから取り込んだデータも Variant型変数 に一括取り込みした場合は 1次2次どちらもLBOUNDは 0 になるので、 これらの配列は使用する時は、別の 1 から始まる配列に書き換えてから使用しないと使えないですね。と言う事です。
それとも私が気付いてないだけで、引数のどれかを変えたらそのまま使えるのかな?
(sy) 2017/07/29(土) 09:17
は!! しまった! その件、すっかり忘れていました。
> LBOUNDが0の時はエラーになりますね。 あれ? 0だろうがマイナススタートだろうが、エラーにはならないですよ・・・?
Sub マジか() Const L1 As Long = 0 Const U1 As Long = 9 Dim v(L1 To U1) As Variant, v2(0, L1 To U1) As Variant, v3(L1 To U1, 0) As Variant Dim i As Long Dim vR(L1 To U1) As Variant
For i = L1 To U1 v(i) = Int(Rnd() * 10) v2(0, i) = v(i) v3(i, 0) = v(i) Next Debug.Print Join(v, ","), "ソート前" Call QuickSort(v, L1, U1) Debug.Print Join(v, ","), "ソート後(1次元)"
Call QuickSort2(v2, 0, L1, U1, , False) For i = L1 To U1 vR(i) = v2(0, i) Next Debug.Print Join(vR, ","), "ソート後(2次元1)"
Call QuickSort2(v3, 0, L1, U1, , True) For i = L1 To U1 vR(i) = v3(i, 0) Next Debug.Print Join(vR, ","), "ソート後(2次元2)"
End Sub
と、思ったのですが、そういう意味じゃないですね。 > Split関数 で分割して Variant型変数に一括取り込みしたような配列 これですね。
「配列として宣言したVariant型変数」にSplit関数を代入しようとしても、 実行時エラー13(型が一致しません)になりますので、 Split関数の受取りには配列として宣言していない「ただのVariant型変数」を使うハズ。
でも私のQuickSortに引数として渡す時には「配列として宣言したVariant型変数」しか 受け付けない様に書いています。
Public Sub QuickSort(Target() As Variant, ・・・ ~~引数の受け付けを明示的配列のみに限定してる
なので「ただのVariant型変数」として宣言された配列をQuickSortに渡そうとしても、 「コンパイルエラー:配列またはユーザー定義型を指定してください。」 となってしまいます。
これのことでしょうか? だとすれば、そこまで考えてませんでしたね〜 RangeのValueを受け取る時はSplit関数の様なエラーは発生しないので気付かなかったです。
試しにソートモジュール内のSubステートメント、Functionステートメントの引数部分にある 「Target() As Variant」をすべて「Target As Variant」に書き変えたら、 「ただのVariant型変数」として宣言された配列でも扱えるようにはなりました。
扱えるようにはなったのですが・・・、遅くなりました。処理時間50〜60%増です。 ダメだなこりゃ。
syさん、最後まで的確なご指摘をありがとうございます。
(中途B) 2017/07/29(土) 17:48
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.